mirror of
https://github.com/coleam00/Archon.git
synced 2025-12-23 18:29:18 -05:00
Remove dead code
This commit is contained in:
@@ -1,20 +1,16 @@
|
||||
import { useState } from "react";
|
||||
import { Palette, Layout, Settings } from "lucide-react";
|
||||
import { Palette, Layout } from "lucide-react";
|
||||
import { PillNavigation, type PillNavigationItem } from "../shared/PillNavigation";
|
||||
import { StyleGuideTab } from "../tabs/StyleGuideTab";
|
||||
import { LayoutsTab } from "../tabs/LayoutsTab";
|
||||
import { ConfiguratorsTab } from "../tabs/ConfiguratorsTab";
|
||||
import { ThemeToggle } from "../../../components/ui/ThemeToggle";
|
||||
|
||||
export const StyleGuideView = () => {
|
||||
const [activeTab, setActiveTab] = useState<"style-guide" | "layouts" | "configurators">(
|
||||
"style-guide",
|
||||
);
|
||||
const [activeTab, setActiveTab] = useState<"style-guide" | "layouts">("style-guide");
|
||||
|
||||
const navigationItems: PillNavigationItem[] = [
|
||||
{ id: "style-guide", label: "Style Guide", icon: <Palette className="w-4 h-4" /> },
|
||||
{ id: "layouts", label: "Layouts", icon: <Layout className="w-4 h-4" /> },
|
||||
{ id: "configurators", label: "Configurators", icon: <Settings className="w-4 h-4" /> },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -29,7 +25,7 @@ export const StyleGuideView = () => {
|
||||
Archon UI Style Guide
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-lg max-w-2xl mx-auto">
|
||||
Design system foundations, layout patterns, and interactive component configurators.
|
||||
Design system foundations and layout patterns for building consistent interfaces.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,7 +47,6 @@ export const StyleGuideView = () => {
|
||||
<div>
|
||||
{activeTab === "style-guide" && <StyleGuideTab />}
|
||||
{activeTab === "layouts" && <LayoutsTab />}
|
||||
{activeTab === "configurators" && <ConfiguratorsTab />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -1,315 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { Checkbox, type CheckboxColor } from "../../../features/ui/primitives/checkbox";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from "../../../features/ui/primitives/select";
|
||||
import { ConfiguratorCard } from "../shared/ConfiguratorCard";
|
||||
import { Label } from "../../../features/ui/primitives/label";
|
||||
|
||||
const colorOptions = [
|
||||
{ value: "purple", label: "Purple" },
|
||||
{ value: "blue", label: "Blue" },
|
||||
{ value: "green", label: "Green" },
|
||||
{ value: "pink", label: "Pink" },
|
||||
{ value: "orange", label: "Orange" },
|
||||
{ value: "cyan", label: "Cyan" },
|
||||
];
|
||||
|
||||
const stateOptions = [
|
||||
{ value: "unchecked", label: "Unchecked" },
|
||||
{ value: "checked", label: "Checked" },
|
||||
{ value: "indeterminate", label: "Indeterminate" },
|
||||
];
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Checkbox Configurator
|
||||
*
|
||||
* CONFIGURATION OPTIONS:
|
||||
* 1. COLOR - Six accent colors with neon glow
|
||||
* - Each color has associated glow effects
|
||||
* - Glow intensity increases on check
|
||||
*
|
||||
* 2. STATE - Three checkbox states
|
||||
* - Unchecked: Empty glass box
|
||||
* - Checked: Check icon with glow
|
||||
* - Indeterminate: Minus icon (partial selection)
|
||||
*
|
||||
* 3. LABEL - Optional label positioning
|
||||
* - Can be placed left or right
|
||||
* - Click target includes label
|
||||
*
|
||||
* 4. DISABLED - Interactive state control
|
||||
*/
|
||||
export function CheckboxConfigurator() {
|
||||
const [color, setColor] = useState<CheckboxColor>("cyan");
|
||||
const [state, setState] = useState<"unchecked" | "checked" | "indeterminate">("checked");
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
const [showLabel, setShowLabel] = useState(true);
|
||||
|
||||
const getCheckedState = () => {
|
||||
if (state === "indeterminate") return "indeterminate";
|
||||
return state === "checked";
|
||||
};
|
||||
|
||||
const generateCode = () => {
|
||||
const props: string[] = [];
|
||||
|
||||
if (color !== "cyan") props.push(`color="${color}"`);
|
||||
if (state === "indeterminate") {
|
||||
props.push(`indeterminate`);
|
||||
props.push(`checked="indeterminate"`);
|
||||
} else if (state === "checked") {
|
||||
props.push(`checked={true}`);
|
||||
} else {
|
||||
props.push(`checked={false}`);
|
||||
}
|
||||
if (disabled) props.push(`disabled`);
|
||||
props.push(`onCheckedChange={(checked) => console.log(checked)}`);
|
||||
|
||||
const checkboxCode = `<Checkbox
|
||||
${props.join("\n ")}
|
||||
/>`;
|
||||
|
||||
if (showLabel) {
|
||||
return `import { Checkbox } from "@/features/ui/primitives/checkbox";
|
||||
import { Label } from "@/features/ui/primitives/label";
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Checkbox with Label
|
||||
*
|
||||
* STATE: ${state}
|
||||
* COLOR: ${color} - Neon glow effect
|
||||
*
|
||||
* GLASS PROPERTIES:
|
||||
* - Transparency: bg-white/10 backdrop-blur
|
||||
* - Glow: ${color} shadow on checked state
|
||||
* - Animation: Zoom in/out on check
|
||||
* ${state === "indeterminate" ? "* Indeterminate: Partial selection state" : ""}
|
||||
*/
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="terms"
|
||||
${props.join("\n ")}
|
||||
/>
|
||||
<Label
|
||||
htmlFor="terms"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Accept terms and conditions
|
||||
</Label>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return `import { Checkbox } from "@/features/ui/primitives/checkbox";
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Standalone Checkbox
|
||||
*
|
||||
* STATE: ${state}
|
||||
* COLOR: ${color} - Neon glow effect
|
||||
*
|
||||
* GLASS PROPERTIES:
|
||||
* - Transparency: bg-white/10 backdrop-blur
|
||||
* - Glow: ${color} shadow on checked state
|
||||
* - Animation: Zoom in/out on check
|
||||
* ${state === "indeterminate" ? "* Indeterminate: Partial selection state" : ""}
|
||||
*/
|
||||
|
||||
${checkboxCode}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfiguratorCard
|
||||
title="Checkbox"
|
||||
description="Checkboxes with neon glow effects and indeterminate state support"
|
||||
code={generateCode()}
|
||||
>
|
||||
<div className="space-y-6">
|
||||
{/* Preview */}
|
||||
<div className="flex items-center justify-center p-8 rounded-lg bg-black/5 dark:bg-white/5">
|
||||
{showLabel ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="preview"
|
||||
color={color}
|
||||
checked={getCheckedState()}
|
||||
indeterminate={state === "indeterminate"}
|
||||
disabled={disabled}
|
||||
onCheckedChange={() => {
|
||||
// Cycle through states
|
||||
if (state === "unchecked") setState("checked");
|
||||
else if (state === "checked") setState("indeterminate");
|
||||
else setState("unchecked");
|
||||
}}
|
||||
/>
|
||||
<Label
|
||||
htmlFor="preview"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer"
|
||||
>
|
||||
Accept terms and conditions
|
||||
</Label>
|
||||
</div>
|
||||
) : (
|
||||
<Checkbox
|
||||
color={color}
|
||||
checked={getCheckedState()}
|
||||
indeterminate={state === "indeterminate"}
|
||||
disabled={disabled}
|
||||
onCheckedChange={() => {
|
||||
// Cycle through states
|
||||
if (state === "unchecked") setState("checked");
|
||||
else if (state === "checked") setState("indeterminate");
|
||||
else setState("unchecked");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Configuration */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{/* Color */}
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
Color
|
||||
</label>
|
||||
<Select value={color} onValueChange={(v) => setColor(v as CheckboxColor)}>
|
||||
<SelectTrigger color={color}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent color={color}>
|
||||
{colorOptions.map(option => (
|
||||
<SelectItem key={option.value} value={option.value} color={color}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* State */}
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
State
|
||||
</label>
|
||||
<Select value={state} onValueChange={(v) => setState(v as any)}>
|
||||
<SelectTrigger color={color}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent color={color}>
|
||||
{stateOptions.map(option => (
|
||||
<SelectItem key={option.value} value={option.value} color={color}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Label */}
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
Label
|
||||
</label>
|
||||
<div className="flex gap-4">
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<Checkbox
|
||||
color={color}
|
||||
checked={showLabel}
|
||||
onCheckedChange={setShowLabel}
|
||||
/>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Show Label</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Disabled */}
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
State
|
||||
</label>
|
||||
<div className="flex gap-4">
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<Checkbox
|
||||
color={color}
|
||||
checked={disabled}
|
||||
onCheckedChange={setDisabled}
|
||||
/>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Disabled</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Examples */}
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-sm font-medium text-gray-700 dark:text-gray-300">Examples</h4>
|
||||
|
||||
{/* Color Grid */}
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">All Colors</p>
|
||||
<div className="flex gap-3">
|
||||
{colorOptions.map(opt => (
|
||||
<Checkbox
|
||||
key={opt.value}
|
||||
color={opt.value as CheckboxColor}
|
||||
defaultChecked
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* States */}
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">States</p>
|
||||
<div className="flex gap-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox color={color} checked={false} />
|
||||
<span className="text-xs text-gray-500">Unchecked</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox color={color} checked={true} />
|
||||
<span className="text-xs text-gray-500">Checked</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox color={color} indeterminate />
|
||||
<span className="text-xs text-gray-500">Indeterminate</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox color={color} checked={true} disabled />
|
||||
<span className="text-xs text-gray-500">Disabled</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* List Example */}
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">List Example</p>
|
||||
<div className="space-y-2 p-4 rounded-lg bg-black/5 dark:bg-white/5">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox id="option1" color={color} defaultChecked />
|
||||
<Label htmlFor="option1" className="text-sm cursor-pointer">Enable notifications</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox id="option2" color={color} />
|
||||
<Label htmlFor="option2" className="text-sm cursor-pointer">Show preview pane</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox id="option3" color={color} defaultChecked />
|
||||
<Label htmlFor="option3" className="text-sm cursor-pointer">Auto-save drafts</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox id="option4" color={color} indeterminate />
|
||||
<Label htmlFor="option4" className="text-sm cursor-pointer">Sync across devices (partial)</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ConfiguratorCard>
|
||||
);
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -1,453 +0,0 @@
|
||||
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 { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
import type { GlowColor, GlowType, EdgePosition, EdgeColor, CardSize, Transparency, BlurLevel, GlassTint } from '../types';
|
||||
|
||||
interface GlassCardConfig {
|
||||
blur: BlurLevel;
|
||||
transparency: Transparency;
|
||||
glassTint: GlassTint;
|
||||
glowColor: GlowColor;
|
||||
glowType: GlowType;
|
||||
edgePosition: EdgePosition;
|
||||
edgeColor: EdgeColor;
|
||||
size: CardSize;
|
||||
content: string;
|
||||
}
|
||||
|
||||
// Glass card showcase examples
|
||||
const GLASS_CARD_EXAMPLES = [
|
||||
{
|
||||
name: 'Clear Glass',
|
||||
transparency: 'light' as const,
|
||||
glassTint: 'none' as const,
|
||||
glowColor: 'none' as const,
|
||||
edgePosition: 'none' as const,
|
||||
description: 'Pure transparent glass',
|
||||
usage: 'Default containers, neutral elements'
|
||||
},
|
||||
{
|
||||
name: 'Purple Glass',
|
||||
transparency: 'medium' as const,
|
||||
glassTint: 'purple' as const,
|
||||
glowColor: 'none' as const,
|
||||
edgePosition: 'none' as const,
|
||||
description: 'Purple-tinted transparent glass',
|
||||
usage: 'Primary content areas'
|
||||
},
|
||||
{
|
||||
name: 'Cyan Inner Glow',
|
||||
transparency: 'light' as const,
|
||||
glassTint: 'none' as const,
|
||||
glowColor: 'cyan' as const,
|
||||
edgePosition: 'none' as const,
|
||||
description: 'Glass with cyan inner glow',
|
||||
usage: 'Active/selected states'
|
||||
},
|
||||
{
|
||||
name: 'Purple Outer Glow',
|
||||
transparency: 'light' as const,
|
||||
glassTint: 'none' as const,
|
||||
glowColor: 'purple' as const,
|
||||
edgePosition: 'none' as const,
|
||||
description: 'Glass with purple outer glow',
|
||||
usage: 'Featured content, CTAs'
|
||||
},
|
||||
{
|
||||
name: 'Purple Edge',
|
||||
transparency: 'light' as const,
|
||||
glassTint: 'none' as const,
|
||||
glowColor: 'none' as const,
|
||||
edgePosition: 'top' as const,
|
||||
description: 'Glass with purple top edge',
|
||||
usage: 'Knowledge cards, headers'
|
||||
},
|
||||
{
|
||||
name: 'Blue Left Edge',
|
||||
transparency: 'light' as const,
|
||||
glassTint: 'blue' as const,
|
||||
glowColor: 'none' as const,
|
||||
edgePosition: 'left' as const,
|
||||
description: 'Light blue glass with blue left edge',
|
||||
usage: 'Task cards, side content'
|
||||
}
|
||||
];
|
||||
|
||||
export const GlassCardConfigurator = () => {
|
||||
const [activeView, setActiveView] = useState<'showcase' | 'configurator'>('showcase');
|
||||
const [config, setConfig] = useState<GlassCardConfig>({
|
||||
blur: 'xl',
|
||||
transparency: 'light',
|
||||
glassTint: 'none',
|
||||
glowColor: 'none',
|
||||
glowType: 'none',
|
||||
edgePosition: 'none',
|
||||
edgeColor: 'cyan',
|
||||
size: 'lg',
|
||||
content: 'Your glass card content here'
|
||||
});
|
||||
|
||||
const generateCode = (config: GlassCardConfig) => {
|
||||
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' && config.glowType === 'inner') 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}"`);
|
||||
|
||||
let additionalClasses = '';
|
||||
if (config.glowType === 'outer' && config.glowColor !== 'none') {
|
||||
additionalClasses = `\n className="shadow-[0_0_30px_${config.glowColor}-500/80,_0_0_60px_${config.glowColor}-500/40]"`;
|
||||
} else if (config.glowType === 'inner' && config.glowColor !== 'none') {
|
||||
additionalClasses = `\n className="bg-${config.glowColor}-500/40 border border-${config.glowColor}-500 shadow-[inset_0_0_20px_${config.glowColor}-500/60]"`;
|
||||
}
|
||||
|
||||
return `import { Card } from '@/features/ui/primitives/card';
|
||||
|
||||
export const MyCard = () => {
|
||||
return (
|
||||
<Card${props.length > 0 ? '\n ' + props.join('\n ') : ''}${additionalClasses}>
|
||||
${config.content}
|
||||
</Card>
|
||||
);
|
||||
};`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* Small Pill Navigation */}
|
||||
<div className="flex justify-center">
|
||||
<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">
|
||||
<div className="flex gap-1 items-center">
|
||||
<button
|
||||
onClick={() => setActiveView('showcase')}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-4 py-2 rounded-full transition-all duration-200",
|
||||
"text-sm font-medium whitespace-nowrap",
|
||||
activeView === 'showcase'
|
||||
? "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"
|
||||
)}
|
||||
>
|
||||
Showcase
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveView('configurator')}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-4 py-2 rounded-full transition-all duration-200",
|
||||
"text-sm font-medium whitespace-nowrap",
|
||||
activeView === 'configurator'
|
||||
? "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"
|
||||
)}
|
||||
>
|
||||
Configurator
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content based on active view */}
|
||||
{activeView === 'showcase' ? (
|
||||
<Card className="p-6">
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">Glass Card Showcase</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{GLASS_CARD_EXAMPLES.map((example) => (
|
||||
<div key={example.name}>
|
||||
{/* Apply proper effects based on example type */}
|
||||
{example.name === 'Cyan Inner Glow' ? (
|
||||
<Card
|
||||
transparency={example.transparency}
|
||||
glassTint={example.glassTint}
|
||||
edgePosition="none"
|
||||
className={cn(
|
||||
"hover:scale-105 transition-transform duration-300",
|
||||
"bg-cyan-500/40 border border-cyan-500 shadow-[inset_0_0_20px_rgba(34,211,238,0.6)]"
|
||||
)}
|
||||
>
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">{example.name}</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm mb-2">{example.description}</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-xs">{example.usage}</p>
|
||||
</Card>
|
||||
) : example.name === 'Purple Outer Glow' ? (
|
||||
<div className="shadow-[0_0_30px_rgba(147,51,234,0.8),_0_0_60px_rgba(147,51,234,0.4)] transition-all duration-200 hover:scale-105 rounded-lg">
|
||||
<Card
|
||||
transparency={example.transparency}
|
||||
glassTint={example.glassTint}
|
||||
edgePosition="none"
|
||||
className="transition-transform duration-300"
|
||||
>
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">{example.name}</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm mb-2">{example.description}</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-xs">{example.usage}</p>
|
||||
</Card>
|
||||
</div>
|
||||
) : example.name === 'Purple Edge' ? (
|
||||
<Card
|
||||
transparency={example.transparency}
|
||||
glassTint={example.glassTint}
|
||||
edgePosition={example.edgePosition}
|
||||
edgeColor="purple"
|
||||
className="hover:scale-105 transition-transform duration-300"
|
||||
>
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">{example.name}</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm mb-2">{example.description}</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-xs">{example.usage}</p>
|
||||
</Card>
|
||||
) : example.name === 'Blue Left Edge' ? (
|
||||
<Card
|
||||
transparency={example.transparency}
|
||||
glassTint={example.glassTint}
|
||||
edgePosition={example.edgePosition}
|
||||
edgeColor="blue"
|
||||
className="hover:scale-105 transition-transform duration-300"
|
||||
>
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">{example.name}</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm mb-2">{example.description}</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-xs">{example.usage}</p>
|
||||
</Card>
|
||||
) : (
|
||||
<Card
|
||||
transparency={example.transparency}
|
||||
glassTint={example.glassTint}
|
||||
glowColor={example.glowColor}
|
||||
edgePosition={example.edgePosition}
|
||||
edgeColor="cyan"
|
||||
className="hover:scale-105 transition-transform duration-300"
|
||||
>
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">{example.name}</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm mb-2">{example.description}</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-xs">{example.usage}</p>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
) : (
|
||||
<div className="space-y-8">
|
||||
{/* Configurator with side-by-side layout */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-6">Card Configurator</h3>
|
||||
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
{/* Left: Configuration Controls */}
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Blur</label>
|
||||
<Select
|
||||
value={config.blur}
|
||||
onValueChange={(value) => setConfig({...config, blur: value as BlurLevel})}
|
||||
>
|
||||
<SelectTrigger className="text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">None</SelectItem>
|
||||
<SelectItem value="sm">Minimal</SelectItem>
|
||||
<SelectItem value="md">Subtle</SelectItem>
|
||||
<SelectItem value="lg">Light</SelectItem>
|
||||
<SelectItem value="xl">Standard</SelectItem>
|
||||
<SelectItem value="2xl">Noticeable</SelectItem>
|
||||
<SelectItem value="3xl">Maximum</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Transparency</label>
|
||||
<Select
|
||||
value={config.transparency}
|
||||
onValueChange={(value) => setConfig({...config, transparency: value as Transparency})}
|
||||
>
|
||||
<SelectTrigger className="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>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Glass Tint</label>
|
||||
<Select
|
||||
value={config.glassTint}
|
||||
onValueChange={(value) => setConfig({...config, glassTint: value as GlassTint})}
|
||||
>
|
||||
<SelectTrigger className="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>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Glow Type</label>
|
||||
<Select
|
||||
value={config.glowType}
|
||||
onValueChange={(value) => setConfig({...config, glowType: value as GlowType, glowColor: value === 'none' ? 'none' : config.glowColor || 'cyan'})}
|
||||
disabled={config.edgePosition !== 'none'}
|
||||
>
|
||||
<SelectTrigger className="text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">None</SelectItem>
|
||||
<SelectItem value="inner">Inner</SelectItem>
|
||||
<SelectItem value="outer">Outer</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Glow Color</label>
|
||||
<Select
|
||||
value={config.glowColor}
|
||||
onValueChange={(value) => setConfig({...config, glowColor: value as GlowColor})}
|
||||
disabled={config.glowType === 'none'}
|
||||
>
|
||||
<SelectTrigger className="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>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Edge Position</label>
|
||||
<Select
|
||||
value={config.edgePosition}
|
||||
onValueChange={(value) => {
|
||||
setConfig({
|
||||
...config,
|
||||
edgePosition: value as EdgePosition,
|
||||
glowType: value !== 'none' ? 'none' : config.glowType
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="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>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Edge Color</label>
|
||||
<Select
|
||||
value={config.edgeColor}
|
||||
onValueChange={(value) => setConfig({...config, edgeColor: value as EdgeColor})}
|
||||
disabled={config.edgePosition === 'none'}
|
||||
>
|
||||
<SelectTrigger className="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>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2">
|
||||
<label className="block text-sm font-medium mb-1">Content</label>
|
||||
<Input
|
||||
value={config.content}
|
||||
onChange={(e) => setConfig({...config, content: e.target.value})}
|
||||
placeholder="Card content..."
|
||||
className="text-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right: Live Preview on grid background */}
|
||||
<div className="flex items-center justify-center">
|
||||
<div
|
||||
className={cn(
|
||||
"transition-all duration-200",
|
||||
// Apply outer glow effect if selected
|
||||
config.glowType === 'outer' && config.glowColor !== 'none' &&
|
||||
`shadow-[0_0_30px_${config.glowColor}-500/80,_0_0_60px_${config.glowColor}-500/40]`
|
||||
)}
|
||||
>
|
||||
<Card
|
||||
blur={config.blur}
|
||||
transparency={config.transparency}
|
||||
glassTint={config.glassTint}
|
||||
glowColor={config.glowType === 'inner' ? config.glowColor : 'none'}
|
||||
edgePosition={config.edgePosition}
|
||||
edgeColor={config.edgeColor}
|
||||
size={config.size}
|
||||
className={cn(
|
||||
// Apply inner glow effect with enhanced visibility
|
||||
config.glowType === 'inner' && config.glowColor !== 'none' && [
|
||||
`bg-${config.glowColor}-500/40 border border-${config.glowColor}-500`,
|
||||
`shadow-[inset_0_0_20px_${config.glowColor}-500/60]`
|
||||
]
|
||||
)}
|
||||
>
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">Glass Card</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm">
|
||||
{config.content}
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Generated Code - Full Width Bottom */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold mb-4">Generated Code</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode(config)}
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,235 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -1,489 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Checkbox } from '@/features/ui/primitives/checkbox';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
import {
|
||||
Home,
|
||||
FileText,
|
||||
BarChart,
|
||||
Settings,
|
||||
ChevronRight,
|
||||
Palette,
|
||||
Component,
|
||||
Layout,
|
||||
Code
|
||||
} from 'lucide-react';
|
||||
|
||||
// Pill Navigation Configurator
|
||||
const PillNavigationConfigurator = () => {
|
||||
const [activeSection, setActiveSection] = useState('foundations');
|
||||
const [activeItem, setActiveItem] = useState('Colors');
|
||||
const [openDropdown, setOpenDropdown] = useState<string | null>('foundations');
|
||||
|
||||
// Configuration options
|
||||
const [colorVariant, setColorVariant] = useState('cyan');
|
||||
const [size, setSize] = useState('default');
|
||||
const [showIcons, setShowIcons] = useState(true);
|
||||
const [showText, setShowText] = useState(true);
|
||||
const [hasSubmenus, setHasSubmenus] = useState(true);
|
||||
|
||||
// Editable navigation items
|
||||
const [navigationItems, setNavigationItems] = useState([
|
||||
{
|
||||
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']
|
||||
},
|
||||
{
|
||||
id: 'patterns',
|
||||
label: 'Patterns',
|
||||
icon: <Layout className="w-4 h-4" />,
|
||||
items: ['Layouts', 'Navigation', 'Data Display']
|
||||
},
|
||||
{
|
||||
id: 'examples',
|
||||
label: 'Examples',
|
||||
icon: <Code className="w-4 h-4" />,
|
||||
items: ['Compositions', 'Pages', 'Workflows']
|
||||
}
|
||||
]);
|
||||
|
||||
const handleSectionClick = (sectionId: string) => {
|
||||
if (activeSection === sectionId && openDropdown === sectionId) {
|
||||
setOpenDropdown(null);
|
||||
} else {
|
||||
setActiveSection(sectionId);
|
||||
if (hasSubmenus) {
|
||||
setOpenDropdown(sectionId);
|
||||
// Set first item as default
|
||||
const section = navigationItems.find(item => item.id === sectionId);
|
||||
if (section?.items?.[0]) {
|
||||
setActiveItem(section.items[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleItemClick = (item: string) => {
|
||||
setActiveItem(item);
|
||||
};
|
||||
|
||||
const updateItemLabel = (itemIndex: number, newLabel: string) => {
|
||||
setNavigationItems(prev =>
|
||||
prev.map((item, index) =>
|
||||
index === itemIndex ? { ...item, label: newLabel } : item
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const getColorClasses = (variant: string, isSelected: boolean) => {
|
||||
const colors = {
|
||||
cyan: 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",
|
||||
purple: isSelected
|
||||
? "bg-purple-500/20 dark:bg-purple-400/20 text-purple-700 dark:text-purple-300 border border-purple-400/50 shadow-[0_0_10px_rgba(147,51,234,0.5)]"
|
||||
: "text-gray-700 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5",
|
||||
emerald: isSelected
|
||||
? "bg-emerald-500/20 dark:bg-emerald-400/20 text-emerald-700 dark:text-emerald-300 border border-emerald-400/50 shadow-[0_0_10px_rgba(16,185,129,0.5)]"
|
||||
: "text-gray-700 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5",
|
||||
orange: isSelected
|
||||
? "bg-orange-500/20 dark:bg-orange-400/20 text-orange-700 dark:text-orange-300 border border-orange-400/50 shadow-[0_0_10px_rgba(251,146,60,0.5)]"
|
||||
: "text-gray-700 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5"
|
||||
};
|
||||
return colors[variant as keyof typeof colors] || colors.cyan;
|
||||
};
|
||||
|
||||
const getSizeClasses = (sizeVariant: string) => {
|
||||
const sizes = {
|
||||
small: "px-4 py-2 text-xs",
|
||||
default: "px-6 py-3 text-sm",
|
||||
large: "px-8 py-4 text-base"
|
||||
};
|
||||
return sizes[sizeVariant as keyof typeof sizes] || sizes.default;
|
||||
};
|
||||
|
||||
const selectedSectionData = navigationItems.find(item => item.id === activeSection);
|
||||
|
||||
const renderPillNavigation = () => (
|
||||
<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 transition-all duration-300 ease-in-out">
|
||||
<div className="flex gap-1 items-center">
|
||||
{navigationItems.map((item) => {
|
||||
const isSelected = activeSection === item.id;
|
||||
const hasDropdown = hasSubmenus && item.items && item.items.length > 0;
|
||||
const isThisExpanded = openDropdown === item.id && hasDropdown;
|
||||
|
||||
return (
|
||||
<div key={item.id} className="relative">
|
||||
{/* Extended pill for selected item with dropdown */}
|
||||
{isSelected && hasDropdown ? (
|
||||
<div className={cn(
|
||||
"flex items-center gap-2 rounded-full transition-all duration-200",
|
||||
"font-medium whitespace-nowrap",
|
||||
getSizeClasses(size),
|
||||
getColorClasses(colorVariant, true)
|
||||
)}>
|
||||
{showIcons && item.icon}
|
||||
{showText && item.label}
|
||||
|
||||
{/* Dropdown selector inside the pill */}
|
||||
<div className="flex items-center ml-4 pl-4 border-l border-current/30">
|
||||
<select
|
||||
value={activeItem || ''}
|
||||
onChange={(e) => handleItemClick(e.target.value)}
|
||||
className="bg-transparent border-none outline-none font-medium cursor-pointer text-inherit"
|
||||
>
|
||||
<option value="" disabled>Select...</option>
|
||||
{item.items?.map((subItem) => (
|
||||
<option key={subItem} value={subItem} className="bg-gray-800 text-white">
|
||||
{subItem}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<ChevronRight
|
||||
className={cn(
|
||||
"w-4 h-4 transition-transform duration-300 ml-2 cursor-pointer",
|
||||
isThisExpanded ? "-rotate-90" : "rotate-0"
|
||||
)}
|
||||
onClick={() => handleSectionClick(item.id)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
/* Regular pill for non-selected items */
|
||||
<button
|
||||
onClick={() => handleSectionClick(item.id)}
|
||||
className={cn(
|
||||
"flex items-center gap-2 rounded-full transition-all duration-200",
|
||||
"font-medium whitespace-nowrap",
|
||||
getSizeClasses(size),
|
||||
getColorClasses(colorVariant, isSelected)
|
||||
)}
|
||||
>
|
||||
{showIcons && item.icon}
|
||||
{showText && item.label}
|
||||
{hasDropdown && (
|
||||
<ChevronRight
|
||||
className={cn(
|
||||
"w-4 h-4 transition-transform duration-300",
|
||||
isThisExpanded ? "-rotate-90" : "rotate-0"
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return {
|
||||
renderPillNavigation,
|
||||
activeSection,
|
||||
activeItem,
|
||||
colorVariant,
|
||||
setColorVariant,
|
||||
size,
|
||||
setSize,
|
||||
showIcons,
|
||||
setShowIcons,
|
||||
showText,
|
||||
setShowText,
|
||||
hasSubmenus,
|
||||
setHasSubmenus,
|
||||
navigationItems,
|
||||
updateItemLabel
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
const generateCode = (configurator: ReturnType<typeof PillNavigationConfigurator>) => {
|
||||
const hasSubmenus = configurator.hasSubmenus;
|
||||
const colorVariant = configurator.colorVariant;
|
||||
const size = configurator.size;
|
||||
const showIcons = configurator.showIcons;
|
||||
const showText = configurator.showText;
|
||||
const navigationItems = configurator.navigationItems;
|
||||
|
||||
const itemsCode = navigationItems.map(item => {
|
||||
const iconName = item.icon?.type?.displayName || item.icon?.type?.name || 'Icon';
|
||||
return ` {
|
||||
id: '${item.id}',
|
||||
label: '${item.label}',${showIcons ? `
|
||||
icon: <${iconName} className="w-4 h-4" />,` : ''}${hasSubmenus ? `
|
||||
items: [${item.items?.map(subItem => `'${subItem}'`).join(', ')}]` : ''}
|
||||
}`;
|
||||
}).join(',\n');
|
||||
|
||||
return `import { useState } from 'react';
|
||||
import { cn } from '@/features/ui/primitives/styles';${hasSubmenus ? `
|
||||
import { ChevronRight } from 'lucide-react';` : ''}${showIcons ? `
|
||||
import { ${navigationItems.map(item => {
|
||||
const iconName = item.icon?.type?.displayName || item.icon?.type?.name || 'Icon';
|
||||
return iconName;
|
||||
}).filter((name, index, arr) => arr.indexOf(name) === index).join(', ')} } from 'lucide-react';` : ''}
|
||||
|
||||
export const PillNavigation = () => {
|
||||
const [activeSection, setActiveSection] = useState('${configurator.activeSection}');${hasSubmenus ? `
|
||||
const [activeItem, setActiveItem] = useState('${configurator.activeItem}');
|
||||
const [openDropdown, setOpenDropdown] = useState<string | null>('${configurator.activeSection}');` : ''}
|
||||
|
||||
const navigationItems = [
|
||||
${itemsCode}
|
||||
];
|
||||
|
||||
const handleSectionClick = (sectionId: string) => {${hasSubmenus ? `
|
||||
if (activeSection === sectionId && openDropdown === sectionId) {
|
||||
setOpenDropdown(null);
|
||||
} else {
|
||||
setActiveSection(sectionId);
|
||||
setOpenDropdown(sectionId);
|
||||
// Set first item as default
|
||||
const section = navigationItems.find(item => item.id === sectionId);
|
||||
if (section?.items?.[0]) {
|
||||
setActiveItem(section.items[0]);
|
||||
}
|
||||
}` : `
|
||||
setActiveSection(sectionId);`}
|
||||
};${hasSubmenus ? `
|
||||
|
||||
const handleItemClick = (item: string) => {
|
||||
setActiveItem(item);
|
||||
};` : ''}
|
||||
|
||||
const getColorClasses = (isSelected: boolean) => {
|
||||
return isSelected
|
||||
? "bg-${colorVariant}-500/20 dark:bg-${colorVariant}-400/20 text-${colorVariant}-700 dark:text-${colorVariant}-300 border border-${colorVariant}-400/50 shadow-[0_0_10px_rgba(${colorVariant === 'cyan' ? '34,211,238' : colorVariant === 'purple' ? '147,51,234' : colorVariant === 'emerald' ? '16,185,129' : '251,146,60'},0.5)]"
|
||||
: "text-gray-700 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5";
|
||||
};${hasSubmenus ? `
|
||||
|
||||
const selectedSectionData = navigationItems.find(item => item.id === activeSection);` : ''}
|
||||
|
||||
return (
|
||||
<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 transition-all duration-300 ease-in-out">
|
||||
<div className="flex gap-1 items-center">
|
||||
{navigationItems.map((item) => {
|
||||
const isSelected = activeSection === item.id;${hasSubmenus ? `
|
||||
const hasDropdown = item.items && item.items.length > 0;
|
||||
const isThisExpanded = openDropdown === item.id && hasDropdown;` : ''}
|
||||
|
||||
return (
|
||||
<div key={item.id} className="relative">${hasSubmenus ? `
|
||||
{/* Extended pill for selected item with dropdown */}
|
||||
{isSelected && hasDropdown ? (
|
||||
<div className={cn(
|
||||
"flex items-center gap-2 ${size === 'small' ? 'px-4 py-2 text-xs' : size === 'large' ? 'px-8 py-4 text-base' : 'px-6 py-3 text-sm'} rounded-full transition-all duration-200",
|
||||
"font-medium whitespace-nowrap",
|
||||
getColorClasses(true)
|
||||
)}>
|
||||
${showIcons ? '{item.icon}' : ''}${showText ? `${showIcons ? ' ' : ''}{item.label}` : ''}
|
||||
|
||||
{/* Dropdown selector inside the pill */}
|
||||
<div className="flex items-center ml-4 pl-4 border-l border-current/30">
|
||||
<select
|
||||
value={activeItem || ''}
|
||||
onChange={(e) => handleItemClick(e.target.value)}
|
||||
className="bg-transparent border-none outline-none font-medium cursor-pointer text-inherit"
|
||||
>
|
||||
<option value="" disabled>Select...</option>
|
||||
{item.items?.map((subItem) => (
|
||||
<option key={subItem} value={subItem} className="bg-gray-800 text-white">
|
||||
{subItem}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<ChevronRight
|
||||
className={cn(
|
||||
"w-4 h-4 transition-transform duration-300 ml-2 cursor-pointer",
|
||||
isThisExpanded ? "-rotate-90" : "rotate-0"
|
||||
)}
|
||||
onClick={() => handleSectionClick(item.id)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
/* Regular pill for non-selected items */` : ''}
|
||||
<button
|
||||
onClick={() => handleSectionClick(item.id)}
|
||||
className={cn(
|
||||
"flex items-center gap-2 ${size === 'small' ? 'px-4 py-2 text-xs' : size === 'large' ? 'px-8 py-4 text-base' : 'px-6 py-3 text-sm'} rounded-full transition-all duration-200",
|
||||
"font-medium whitespace-nowrap",
|
||||
getColorClasses(isSelected)
|
||||
)}
|
||||
>
|
||||
${showIcons ? '{item.icon}' : ''}${showText ? `${showIcons ? ' ' : ''}{item.label}` : ''}${hasSubmenus ? `
|
||||
{hasDropdown && (
|
||||
<ChevronRight
|
||||
className={cn(
|
||||
"w-4 h-4 transition-transform duration-300",
|
||||
isThisExpanded ? "-rotate-90" : "rotate-0"
|
||||
)}
|
||||
/>
|
||||
)}` : ''}
|
||||
</button>${hasSubmenus ? `
|
||||
)}` : ''}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};`;
|
||||
};
|
||||
|
||||
export const NavigationPattern = () => {
|
||||
const configurator = PillNavigationConfigurator();
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* Header Description */}
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Pill Navigation</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Modern glassmorphism pill-style navigation with optional sub-menu support. Perfect for primary navigation,
|
||||
tab switching, and section selection with hierarchical options. Features an extended pill design for selected
|
||||
items with internal dropdown selectors.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Full Width Live Preview */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold mb-4">Live Preview</h3>
|
||||
<div className="flex items-center justify-center py-8">
|
||||
{configurator.renderPillNavigation()}
|
||||
</div>
|
||||
|
||||
{/* Current Selection Display */}
|
||||
{configurator.hasSubmenus && (
|
||||
<div className="mt-6 text-center p-4 bg-gray-50 dark:bg-gray-800/30 rounded-lg">
|
||||
<h4 className="font-medium text-gray-900 dark:text-gray-100">Current Selection</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
Section: <span className="font-medium">{configurator.activeSection}</span>
|
||||
{configurator.activeItem && (
|
||||
<> | Item: <span className="font-medium">{configurator.activeItem}</span></>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* Split Layout: Configurator + Code */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
|
||||
{/* Left Half: Configuration Controls */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold mb-4">Configuration Options</h3>
|
||||
<div className="space-y-4">
|
||||
|
||||
{/* Color and Size */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Color Variant</label>
|
||||
<select
|
||||
value={configurator.colorVariant}
|
||||
onChange={(e) => configurator.setColorVariant(e.target.value)}
|
||||
className="w-full p-2 text-sm border rounded-md bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600"
|
||||
>
|
||||
<option value="cyan">Cyan</option>
|
||||
<option value="purple">Purple</option>
|
||||
<option value="emerald">Emerald</option>
|
||||
<option value="orange">Orange</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Size</label>
|
||||
<select
|
||||
value={configurator.size}
|
||||
onChange={(e) => configurator.setSize(e.target.value)}
|
||||
className="w-full p-2 text-sm border rounded-md bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600"
|
||||
>
|
||||
<option value="small">Small</option>
|
||||
<option value="default">Default</option>
|
||||
<option value="large">Large</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Checkboxes */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id="show-icons"
|
||||
checked={configurator.showIcons}
|
||||
onCheckedChange={configurator.setShowIcons}
|
||||
color="cyan"
|
||||
/>
|
||||
<label htmlFor="show-icons" className="text-sm font-medium">
|
||||
Show Icons
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id="show-text"
|
||||
checked={configurator.showText}
|
||||
onCheckedChange={configurator.setShowText}
|
||||
color="cyan"
|
||||
/>
|
||||
<label htmlFor="show-text" className="text-sm font-medium">
|
||||
Show Text
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submenu toggle */}
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id="has-submenus"
|
||||
checked={configurator.hasSubmenus}
|
||||
onCheckedChange={configurator.setHasSubmenus}
|
||||
color="cyan"
|
||||
/>
|
||||
<label htmlFor="has-submenus" className="text-sm font-medium">
|
||||
Enable Sub-menus
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Editable Navigation Items */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Navigation Items</label>
|
||||
<div className="space-y-2">
|
||||
{configurator.navigationItems.map((item, index) => (
|
||||
<input
|
||||
key={item.id}
|
||||
type="text"
|
||||
value={item.label}
|
||||
onChange={(e) => configurator.updateItemLabel(index, e.target.value)}
|
||||
className="w-full p-2 text-sm border rounded-md bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600"
|
||||
placeholder={`Item ${index + 1} label`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Right Half: Generated Code */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold mb-4">Generated Code</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode(configurator)}
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,319 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Moon, Sun, Power, Wifi, WifiOff,
|
||||
Volume2, VolumeX, Bell, BellOff,
|
||||
Eye, EyeOff, Lock, Unlock
|
||||
} from "lucide-react";
|
||||
import { Switch, type SwitchSize, type SwitchColor } from "../../../features/ui/primitives/switch";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from "../../../features/ui/primitives/select";
|
||||
import { ConfiguratorCard } from "../shared/ConfiguratorCard";
|
||||
|
||||
const sizeOptions = [
|
||||
{ value: "sm", label: "Small (16px)" },
|
||||
{ value: "md", label: "Medium (24px)" },
|
||||
{ value: "lg", label: "Large (32px)" },
|
||||
];
|
||||
|
||||
const colorOptions = [
|
||||
{ value: "purple", label: "Purple" },
|
||||
{ value: "blue", label: "Blue" },
|
||||
{ value: "green", label: "Green" },
|
||||
{ value: "pink", label: "Pink" },
|
||||
{ value: "orange", label: "Orange" },
|
||||
{ value: "cyan", label: "Cyan" },
|
||||
];
|
||||
|
||||
const iconOptions = [
|
||||
{ value: "none", label: "No Icon", iconOn: null, iconOff: null },
|
||||
{ value: "theme", label: "Theme", iconOn: <Moon className="w-full h-full" />, iconOff: <Sun className="w-full h-full" /> },
|
||||
{ value: "power", label: "Power", iconOn: <Power className="w-full h-full" />, iconOff: <Power className="w-full h-full" /> },
|
||||
{ value: "wifi", label: "WiFi", iconOn: <Wifi className="w-full h-full" />, iconOff: <WifiOff className="w-full h-full" /> },
|
||||
{ value: "sound", label: "Sound", iconOn: <Volume2 className="w-full h-full" />, iconOff: <VolumeX className="w-full h-full" /> },
|
||||
{ value: "notifications", label: "Notifications", iconOn: <Bell className="w-full h-full" />, iconOff: <BellOff className="w-full h-full" /> },
|
||||
{ value: "visibility", label: "Visibility", iconOn: <Eye className="w-full h-full" />, iconOff: <EyeOff className="w-full h-full" /> },
|
||||
{ value: "lock", label: "Lock", iconOn: <Lock className="w-full h-full" />, iconOff: <Unlock className="w-full h-full" /> },
|
||||
];
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Switch Configurator
|
||||
*
|
||||
* CONFIGURATION OPTIONS:
|
||||
* 1. SIZE - Three variants for different use cases
|
||||
* - Small: Clean minimal switches for dense UIs
|
||||
* - Medium: Standard switches with optional icons
|
||||
* - Large: Feature toggles with prominent icons
|
||||
*
|
||||
* 2. COLOR - Six accent colors matching the design system
|
||||
* - Each color has associated glow effects
|
||||
*
|
||||
* 3. ICONS - Dynamic icon switching
|
||||
* - Different icons for on/off states
|
||||
* - Icons scale based on size variant
|
||||
*
|
||||
* 4. STATE - Interactive toggle preview
|
||||
* - Live preview updates as configuration changes
|
||||
*/
|
||||
export function SwitchConfigurator() {
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [size, setSize] = useState<SwitchSize>("lg");
|
||||
const [color, setColor] = useState<SwitchColor>("cyan");
|
||||
const [iconOption, setIconOption] = useState("theme");
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
const [transparency, setTransparency] = useState("medium");
|
||||
const [glowIntensity, setGlowIntensity] = useState("normal");
|
||||
const [iconColorSync, setIconColorSync] = useState(true);
|
||||
|
||||
const selectedIcon = iconOptions.find(opt => opt.value === iconOption);
|
||||
|
||||
const generateCode = () => {
|
||||
const imports = ["Switch"];
|
||||
const props: string[] = [];
|
||||
|
||||
if (size !== "md") props.push(`size="${size}"`);
|
||||
if (color !== "cyan") props.push(`color="${color}"`);
|
||||
if (selectedIcon?.iconOn && selectedIcon?.iconOff) {
|
||||
if (selectedIcon.value === "theme") {
|
||||
imports.push("Moon", "Sun");
|
||||
props.push(`iconOn={<Moon className="w-full h-full" />}`);
|
||||
props.push(`iconOff={<Sun className="w-full h-full" />}`);
|
||||
} else if (selectedIcon.value === "power") {
|
||||
imports.push("Power");
|
||||
props.push(`icon={<Power className="w-full h-full" />}`);
|
||||
} else if (selectedIcon.value === "wifi") {
|
||||
imports.push("Wifi", "WifiOff");
|
||||
props.push(`iconOn={<Wifi className="w-full h-full" />}`);
|
||||
props.push(`iconOff={<WifiOff className="w-full h-full" />}`);
|
||||
}
|
||||
// Add other icon cases as needed
|
||||
}
|
||||
if (disabled) props.push(`disabled`);
|
||||
props.push(`checked={checked}`);
|
||||
props.push(`onCheckedChange={setChecked}`);
|
||||
|
||||
const iconImports = imports.filter(i => i !== "Switch").length > 0
|
||||
? `import { ${imports.filter(i => i !== "Switch").join(", ")} } from "lucide-react";\n`
|
||||
: "";
|
||||
|
||||
return `${iconImports}import { Switch } from "@/features/ui/primitives/switch";
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Switch Component
|
||||
*
|
||||
* SIZE: ${size} - ${sizeOptions.find(s => s.value === size)?.label}
|
||||
* COLOR: ${color} - Neon glow effect
|
||||
* ICONS: ${iconOption === "none" ? "No icons" : `${selectedIcon?.label} icons`}
|
||||
* ${size === "sm" ? "* Note: Small switches don't display icons" : ""}
|
||||
*
|
||||
* GLASS PROPERTIES:
|
||||
* - Transparency: bg-white/10 backdrop-blur
|
||||
* - Glow: ${color} shadow on checked state
|
||||
* - Transition: 500ms cubic-bezier animation
|
||||
*/
|
||||
|
||||
<Switch
|
||||
${props.join("\n ")}
|
||||
/>`;
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfiguratorCard
|
||||
title="Switch"
|
||||
description="Toggle switches with size variants, icons, and neon glow effects"
|
||||
code={generateCode()}
|
||||
>
|
||||
<div className="space-y-6">
|
||||
{/* Preview */}
|
||||
<div className="flex items-center justify-center p-8 rounded-lg bg-black/5 dark:bg-white/5">
|
||||
<Switch
|
||||
size={size}
|
||||
color={color}
|
||||
iconOn={selectedIcon?.iconOn}
|
||||
iconOff={selectedIcon?.iconOff}
|
||||
checked={checked}
|
||||
onCheckedChange={setChecked}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Configuration */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{/* Size */}
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
Size
|
||||
</label>
|
||||
<Select value={size} onValueChange={(v) => setSize(v as SwitchSize)}>
|
||||
<SelectTrigger color={color}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent color={color}>
|
||||
{sizeOptions.map(option => (
|
||||
<SelectItem key={option.value} value={option.value} color={color}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Color */}
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
Color
|
||||
</label>
|
||||
<Select value={color} onValueChange={(v) => setColor(v as SwitchColor)}>
|
||||
<SelectTrigger color={color}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent color={color}>
|
||||
{colorOptions.map(option => (
|
||||
<SelectItem key={option.value} value={option.value} color={color}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Icon */}
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
Icon {size === "sm" && "(Not shown for small)"}
|
||||
</label>
|
||||
<Select value={iconOption} onValueChange={setIconOption}>
|
||||
<SelectTrigger color={color}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent color={color}>
|
||||
{iconOptions.map(option => (
|
||||
<SelectItem key={option.value} value={option.value} color={color}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Transparency */}
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
Transparency
|
||||
</label>
|
||||
<Select value={transparency} onValueChange={setTransparency}>
|
||||
<SelectTrigger color={color}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent color={color}>
|
||||
<SelectItem value="low" color={color}>Low (More opaque)</SelectItem>
|
||||
<SelectItem value="medium" color={color}>Medium</SelectItem>
|
||||
<SelectItem value="high" color={color}>High (More transparent)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Glow Intensity */}
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
Glow Intensity
|
||||
</label>
|
||||
<Select value={glowIntensity} onValueChange={setGlowIntensity}>
|
||||
<SelectTrigger color={color}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent color={color}>
|
||||
<SelectItem value="none" color={color}>None</SelectItem>
|
||||
<SelectItem value="subtle" color={color}>Subtle</SelectItem>
|
||||
<SelectItem value="normal" color={color}>Normal</SelectItem>
|
||||
<SelectItem value="intense" color={color}>Intense</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* State & Icon Color */}
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
||||
Options
|
||||
</label>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={disabled}
|
||||
onChange={(e) => setDisabled(e.target.checked)}
|
||||
className="sr-only"
|
||||
/>
|
||||
<Switch
|
||||
size="sm"
|
||||
color={color}
|
||||
checked={disabled}
|
||||
onCheckedChange={setDisabled}
|
||||
/>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Disabled</span>
|
||||
</label>
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={iconColorSync}
|
||||
onChange={(e) => setIconColorSync(e.target.checked)}
|
||||
className="sr-only"
|
||||
/>
|
||||
<Switch
|
||||
size="sm"
|
||||
color={color}
|
||||
checked={iconColorSync}
|
||||
onCheckedChange={setIconColorSync}
|
||||
/>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Icon color syncs</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Examples */}
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-sm font-medium text-gray-700 dark:text-gray-300">Examples</h4>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">Small</p>
|
||||
<div className="flex gap-2">
|
||||
{colorOptions.slice(0, 3).map(opt => (
|
||||
<Switch
|
||||
key={opt.value}
|
||||
size="sm"
|
||||
color={opt.value as SwitchColor}
|
||||
defaultChecked
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">Medium with Icons</p>
|
||||
<div className="flex gap-2">
|
||||
<Switch size="md" color="purple" iconOn={<Moon />} iconOff={<Sun />} defaultChecked />
|
||||
<Switch size="md" color="green" icon={<Wifi />} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">Large Feature</p>
|
||||
<div className="flex gap-2">
|
||||
<Switch
|
||||
size="lg"
|
||||
color="blue"
|
||||
iconOn={<Power />}
|
||||
iconOff={<Power />}
|
||||
defaultChecked
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ConfiguratorCard>
|
||||
);
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -1,219 +0,0 @@
|
||||
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, Moon, Sun, Volume2, VolumeX, Wifi, WifiOff } 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 space-y-6">
|
||||
{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>
|
||||
|
||||
{/* Icon Switch Examples */}
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-sm font-semibold text-gray-900 dark:text-white">Switches with Icons</h4>
|
||||
<LivePreview>
|
||||
<div className="space-y-6">
|
||||
{/* Dark Mode Toggle */}
|
||||
<div className="flex items-center gap-3">
|
||||
<Label>Dark Mode</Label>
|
||||
<Switch
|
||||
size="lg"
|
||||
color="purple"
|
||||
iconOn={<Sun className="w-5 h-5" />}
|
||||
iconOff={<Moon className="w-5 h-5" />}
|
||||
defaultChecked
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Volume Toggle */}
|
||||
<div className="flex items-center gap-3">
|
||||
<Label>Audio</Label>
|
||||
<Switch
|
||||
size="md"
|
||||
color="blue"
|
||||
iconOn={<Volume2 className="w-3 h-3" />}
|
||||
iconOff={<VolumeX className="w-3 h-3" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* WiFi Toggle */}
|
||||
<div className="flex items-center gap-3">
|
||||
<Label>WiFi</Label>
|
||||
<Switch
|
||||
size="md"
|
||||
color="cyan"
|
||||
iconOn={<Wifi className="w-3 h-3" />}
|
||||
iconOff={<WifiOff className="w-3 h-3" />}
|
||||
defaultChecked
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</LivePreview>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<CodeDisplay
|
||||
code={generateCode(config)}
|
||||
showLineNumbers
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,368 +0,0 @@
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { useState } from 'react';
|
||||
import { Copy, Check } from 'lucide-react';
|
||||
|
||||
// Core color palette with neon variations
|
||||
const CORE_COLORS = [
|
||||
{
|
||||
name: 'Cyan',
|
||||
base: '#06b6d4',
|
||||
variants: {
|
||||
50: '#ecfeff',
|
||||
100: '#cffafe',
|
||||
200: '#a5f3fc',
|
||||
300: '#67e8f9',
|
||||
400: '#22d3ee',
|
||||
500: '#06b6d4',
|
||||
600: '#0891b2',
|
||||
700: '#0e7490',
|
||||
800: '#155e75',
|
||||
900: '#164e63'
|
||||
},
|
||||
usage: 'Primary brand, active states, focus indicators'
|
||||
},
|
||||
{
|
||||
name: 'Blue',
|
||||
base: '#3b82f6',
|
||||
variants: {
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
200: '#bfdbfe',
|
||||
300: '#93c5fd',
|
||||
400: '#60a5fa',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8',
|
||||
800: '#1e40af',
|
||||
900: '#1e3a8a'
|
||||
},
|
||||
usage: 'Information, links, secondary actions'
|
||||
},
|
||||
{
|
||||
name: 'Purple',
|
||||
base: '#a855f7',
|
||||
variants: {
|
||||
50: '#faf5ff',
|
||||
100: '#f3e8ff',
|
||||
200: '#e9d5ff',
|
||||
300: '#d8b4fe',
|
||||
400: '#c084fc',
|
||||
500: '#a855f7',
|
||||
600: '#9333ea',
|
||||
700: '#7c3aed',
|
||||
800: '#6b21a8',
|
||||
900: '#581c87'
|
||||
},
|
||||
usage: 'Secondary actions, creative elements, highlights'
|
||||
},
|
||||
{
|
||||
name: 'Emerald',
|
||||
base: '#10b981',
|
||||
variants: {
|
||||
50: '#ecfdf5',
|
||||
100: '#d1fae5',
|
||||
200: '#a7f3d0',
|
||||
300: '#6ee7b7',
|
||||
400: '#34d399',
|
||||
500: '#10b981',
|
||||
600: '#059669',
|
||||
700: '#047857',
|
||||
800: '#065f46',
|
||||
900: '#064e3b'
|
||||
},
|
||||
usage: 'Success states, positive feedback, growth indicators'
|
||||
},
|
||||
{
|
||||
name: 'Orange',
|
||||
base: '#f97316',
|
||||
variants: {
|
||||
50: '#fff7ed',
|
||||
100: '#ffedd5',
|
||||
200: '#fed7aa',
|
||||
300: '#fdba74',
|
||||
400: '#fb923c',
|
||||
500: '#f97316',
|
||||
600: '#ea580c',
|
||||
700: '#c2410c',
|
||||
800: '#9a3412',
|
||||
900: '#7c2d12'
|
||||
},
|
||||
usage: 'Warnings, notifications, energy elements'
|
||||
},
|
||||
{
|
||||
name: 'Pink',
|
||||
base: '#ec4899',
|
||||
variants: {
|
||||
50: '#fdf2f8',
|
||||
100: '#fce7f3',
|
||||
200: '#fbcfe8',
|
||||
300: '#f9a8d4',
|
||||
400: '#f472b6',
|
||||
500: '#ec4899',
|
||||
600: '#db2777',
|
||||
700: '#be185d',
|
||||
800: '#9d174d',
|
||||
900: '#831843'
|
||||
},
|
||||
usage: 'Special features, premium content, creativity'
|
||||
},
|
||||
{
|
||||
name: 'Red',
|
||||
base: '#ef4444',
|
||||
variants: {
|
||||
50: '#fef2f2',
|
||||
100: '#fee2e2',
|
||||
200: '#fecaca',
|
||||
300: '#fca5a5',
|
||||
400: '#f87171',
|
||||
500: '#ef4444',
|
||||
600: '#dc2626',
|
||||
700: '#b91c1c',
|
||||
800: '#991b1b',
|
||||
900: '#7f1d1d'
|
||||
},
|
||||
usage: 'Errors, dangerous actions, critical alerts'
|
||||
}
|
||||
];
|
||||
|
||||
// Effect types for demonstration
|
||||
const EFFECT_TYPES = [
|
||||
{ name: 'Solid', key: 'solid' },
|
||||
{ name: 'Border', key: 'border' },
|
||||
{ name: 'Inner Glow', key: 'inner-glow' },
|
||||
{ name: 'Outer Glow', key: 'outer-glow' },
|
||||
{ name: 'Text', key: 'text' }
|
||||
];
|
||||
|
||||
// Interactive Color Swatch with Selector and Slider
|
||||
const InteractiveColorSwatch = ({ color }: { color: any }) => {
|
||||
const [selectedVariant, setSelectedVariant] = useState(500);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const currentColor = color.variants[selectedVariant as keyof typeof color.variants];
|
||||
|
||||
const handleCopy = () => {
|
||||
navigator.clipboard.writeText(currentColor);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white">{color.name}</h3>
|
||||
<div
|
||||
className="w-8 h-8 rounded-full border-2 border-gray-300 dark:border-gray-600 cursor-pointer transition-transform hover:scale-110"
|
||||
style={{ backgroundColor: currentColor }}
|
||||
onClick={handleCopy}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-600 dark:text-gray-400 text-sm">{color.usage}</p>
|
||||
|
||||
{/* Color Selector with Current Swatch */}
|
||||
<div
|
||||
className="group relative rounded-lg overflow-hidden cursor-pointer transition-transform hover:scale-105 border border-gray-200 dark:border-gray-700"
|
||||
onClick={handleCopy}
|
||||
>
|
||||
<div
|
||||
className="h-20 w-full transition-all duration-200"
|
||||
style={{ backgroundColor: currentColor }}
|
||||
/>
|
||||
<div className="p-3 bg-white dark:bg-gray-800">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900 dark:text-white">{color.name}-{selectedVariant}</p>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400 font-mono">{currentColor}</p>
|
||||
</div>
|
||||
<div className="opacity-60 group-hover:opacity-100 transition-opacity">
|
||||
{copied ? (
|
||||
<Check className="w-4 h-4 text-green-500" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4 text-gray-400" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Variant Slider */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Color Weight: {selectedVariant}</label>
|
||||
<input
|
||||
type="range"
|
||||
min="100"
|
||||
max="900"
|
||||
step="100"
|
||||
value={selectedVariant}
|
||||
onChange={(e) => setSelectedVariant(Number(e.target.value))}
|
||||
className="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer"
|
||||
style={{
|
||||
background: `linear-gradient(to right,
|
||||
${color.variants[100]},
|
||||
${color.variants[200]},
|
||||
${color.variants[300]},
|
||||
${color.variants[400]},
|
||||
${color.variants[500]},
|
||||
${color.variants[600]},
|
||||
${color.variants[700]},
|
||||
${color.variants[800]},
|
||||
${color.variants[900]})`
|
||||
}}
|
||||
/>
|
||||
<div className="flex justify-between text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
<span>100</span>
|
||||
<span>500</span>
|
||||
<span>900</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Enhanced Effect demonstration component with improved glow
|
||||
const EffectDemo = ({ color, effectType }: { color: any, effectType: string }) => {
|
||||
const getEffectStyles = () => {
|
||||
switch (effectType) {
|
||||
case 'solid':
|
||||
return {
|
||||
backgroundColor: color.base,
|
||||
color: 'white'
|
||||
};
|
||||
case 'border':
|
||||
return {
|
||||
border: `2px solid ${color.base}`,
|
||||
backgroundColor: 'transparent',
|
||||
color: color.base
|
||||
};
|
||||
case 'inner-glow':
|
||||
return {
|
||||
backgroundColor: `${color.base}40`, // More opaque for visibility
|
||||
border: `1px solid ${color.base}`,
|
||||
boxShadow: `inset 0 0 20px ${color.base}60`,
|
||||
color: color.base
|
||||
};
|
||||
case 'outer-glow':
|
||||
return {
|
||||
backgroundColor: `${color.base}20`,
|
||||
border: `1px solid ${color.base}`,
|
||||
boxShadow: `0 0 30px ${color.base}80, 0 0 60px ${color.base}40`,
|
||||
color: color.base
|
||||
};
|
||||
case 'text':
|
||||
return {
|
||||
color: color.base,
|
||||
backgroundColor: 'transparent'
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const getEffectName = () => {
|
||||
switch (effectType) {
|
||||
case 'solid': return 'Solid';
|
||||
case 'border': return 'Border';
|
||||
case 'inner-glow': return 'Inner Glow';
|
||||
case 'outer-glow': return 'Outer Glow';
|
||||
case 'text': return 'Text';
|
||||
default: return effectType;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="p-4 rounded-lg text-center transition-all duration-200 hover:scale-105"
|
||||
style={getEffectStyles()}
|
||||
>
|
||||
<p className="text-sm font-medium">{getEffectName()}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ColorsFoundation = () => {
|
||||
const [copiedColor, setCopiedColor] = useState<string | null>(null);
|
||||
|
||||
const handleColorCopy = (color: string) => {
|
||||
navigator.clipboard.writeText(color);
|
||||
setCopiedColor(color);
|
||||
setTimeout(() => setCopiedColor(null), 2000);
|
||||
};
|
||||
|
||||
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">Color System</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-lg max-w-3xl mx-auto">
|
||||
Neon-inspired color palette with solid fills, borders, glows, and text treatments for Tron-style interfaces.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Semantic Colors Overview */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 mb-12">
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 mx-auto bg-gray-900 dark:bg-gray-100 rounded-lg mb-2"></div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100">Primary</h3>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">#111827</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 mx-auto bg-gray-700 dark:bg-gray-300 rounded-lg mb-2"></div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100">Secondary</h3>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">#374151</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 mx-auto bg-blue-500 rounded-lg mb-2 shadow-[0_0_20px_rgba(59,130,246,0.4)]"></div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100">Accent</h3>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">#3b82f6</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 mx-auto bg-emerald-500 rounded-lg mb-2 shadow-[0_0_20px_rgba(16,185,129,0.4)]"></div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100">Success</h3>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">#10b981</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 mx-auto bg-orange-500 rounded-lg mb-2 shadow-[0_0_20px_rgba(249,115,22,0.4)]"></div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100">Warning</h3>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">#f97316</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 mx-auto bg-red-500 rounded-lg mb-2 shadow-[0_0_20px_rgba(239,68,68,0.4)]"></div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100">Error</h3>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">#ef4444</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Interactive Color Palette */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{CORE_COLORS.map((color) => (
|
||||
<Card key={color.name} className="p-6">
|
||||
<InteractiveColorSwatch color={color} />
|
||||
|
||||
{/* Effect Demonstrations */}
|
||||
<div className="mt-6 space-y-3">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-white">Effects</h4>
|
||||
<div className="grid grid-cols-1 gap-3">
|
||||
{EFFECT_TYPES.map((effect) => (
|
||||
<EffectDemo
|
||||
key={effect.key}
|
||||
color={color}
|
||||
effectType={effect.key}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
{/* Copy Notification */}
|
||||
{copiedColor && (
|
||||
<div className="fixed bottom-4 right-4 bg-green-500 text-white px-4 py-2 rounded-lg shadow-lg">
|
||||
Copied {copiedColor} to clipboard!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,645 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
import { Loader2, Zap, Sparkles, MousePointer } from 'lucide-react';
|
||||
|
||||
// Add Tron animations to the page
|
||||
const addTronAnimations = () => {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes dataStream {
|
||||
0%, 100% { transform: translateY(-100%); }
|
||||
50% { transform: translateY(100%); }
|
||||
}
|
||||
|
||||
@keyframes progressSweep {
|
||||
0%, 100% { background: linear-gradient(to right, #3b82f6, #ef4444, #3b82f6); }
|
||||
50% { background: linear-gradient(to right, #ef4444, #3b82f6, #ef4444); }
|
||||
}
|
||||
|
||||
@keyframes lightTrail {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(400%); }
|
||||
}
|
||||
|
||||
@keyframes energyCascade {
|
||||
0% { transform: translateX(-100%) skewX(12deg); }
|
||||
100% { transform: translateX(200%) skewX(12deg); }
|
||||
}
|
||||
|
||||
@keyframes recognitionScan {
|
||||
0%, 100% { height: 0.5rem; opacity: 0.3; }
|
||||
50% { height: 2rem; opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes spectrumWave {
|
||||
0%, 100% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
}
|
||||
|
||||
@keyframes sparkRace {
|
||||
0% {
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
25% {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
25.01% {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
top: 100%;
|
||||
left: 100%;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
50.01% {
|
||||
top: 100%;
|
||||
left: 100%;
|
||||
transform: translateX(0);
|
||||
}
|
||||
75% {
|
||||
top: 100%;
|
||||
left: 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
75.01% {
|
||||
top: 100%;
|
||||
left: 0;
|
||||
transform: translateY(0);
|
||||
}
|
||||
100% {
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-spin-reverse {
|
||||
animation: spin 1s linear infinite reverse;
|
||||
}
|
||||
|
||||
.animate-dataStream {
|
||||
animation: dataStream 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-progressSweep {
|
||||
animation: progressSweep 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-lightTrail {
|
||||
animation: lightTrail 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-energyCascade {
|
||||
animation: energyCascade 2s linear infinite;
|
||||
}
|
||||
|
||||
.animate-recognitionScan {
|
||||
animation: recognitionScan 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-spectrumWave {
|
||||
animation: spectrumWave 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-borderRace {
|
||||
animation: borderRace 2s linear infinite;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
return () => style.remove();
|
||||
};
|
||||
|
||||
const EFFECT_OPTIONS = [
|
||||
'Interaction',
|
||||
'Loading',
|
||||
'Neon',
|
||||
'Animation'
|
||||
];
|
||||
|
||||
export const EffectsFoundation = () => {
|
||||
const [activeOption, setActiveOption] = useState('Interaction');
|
||||
const [selectedEffect, setSelectedEffect] = useState('hover-glow');
|
||||
|
||||
// Add custom animations when component mounts
|
||||
useEffect(() => {
|
||||
const cleanup = addTronAnimations();
|
||||
return cleanup;
|
||||
}, []);
|
||||
|
||||
// Option 1: Hover & Interaction Effects
|
||||
const renderOption1 = () => (
|
||||
<div className="space-y-8">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<MousePointer className="w-5 h-5" />
|
||||
Hover & Interaction Effects
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{/* Racing Border Effect */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Racing Border</h4>
|
||||
<div className="w-20 h-12 mx-auto bg-black border border-gray-600 rounded-lg cursor-pointer group relative flex items-center justify-center text-xs text-gray-300">
|
||||
<span className="relative z-10">Hover Me</span>
|
||||
{/* Racing spark */}
|
||||
<div className="absolute w-2 h-0.5 bg-blue-400 shadow-[0_0_8px_rgba(59,130,246,1)] opacity-0 group-hover:opacity-100 group-hover:animate-[sparkRace_2s_linear_infinite] rounded-full"></div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Blue Glass Button */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Blue Glass</h4>
|
||||
<button className="backdrop-blur-sm bg-blue-500/20 border border-blue-500/50 hover:bg-blue-500/30 hover:border-blue-500 hover:shadow-[0_0_25px_rgba(59,130,246,0.8)] transition-all duration-300 text-blue-300 hover:text-blue-200 px-4 py-2 rounded-full text-sm font-medium">
|
||||
Glass Button
|
||||
</button>
|
||||
</Card>
|
||||
|
||||
{/* Red Alert Hover */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Red Alert</h4>
|
||||
<div className="w-16 h-16 mx-auto bg-red-500/20 border border-red-500 rounded-lg cursor-pointer hover:scale-110 hover:shadow-[0_0_30px_rgba(239,68,68,0.8)] transition-all duration-200 flex items-center justify-center text-red-300 text-xs">Alert</div>
|
||||
</Card>
|
||||
|
||||
{/* Neon Text Glow */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Neon Text</h4>
|
||||
<div className="text-blue-400 cursor-pointer hover:drop-shadow-[0_0_12px_rgba(59,130,246,1)] transition-all duration-300 font-bold text-lg">
|
||||
BLUE NEON
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Program Recognition */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Program ID</h4>
|
||||
<div className="w-20 h-12 mx-auto bg-black border border-blue-500/50 rounded-lg cursor-pointer hover:border-red-500 hover:shadow-[0_0_20px_rgba(239,68,68,0.6)] transition-all duration-500 flex items-center justify-center text-xs text-gray-300">
|
||||
ID: 7364
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Pulse Activation */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Pulse Hover</h4>
|
||||
<div className="w-20 h-12 mx-auto bg-purple-500/20 border border-purple-500 rounded-lg cursor-pointer hover:animate-pulse hover:shadow-[0_0_30px_rgba(168,85,247,0.8)] transition-all duration-300 flex items-center justify-center text-purple-300 text-xs font-medium">
|
||||
Pulse Me
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Option 2: Loading & Skeleton Effects
|
||||
const renderOption2 = () => (
|
||||
<div className="space-y-8">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Loader2 className="w-5 h-5" />
|
||||
Loading & Skeleton Effects
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{/* Light Cycle Circuit */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Light Cycle Circuit</h4>
|
||||
<div className="w-16 h-16 mx-auto relative">
|
||||
<div className="absolute inset-0 border-2 border-transparent border-t-blue-400 border-r-red-500 rounded-full animate-spin shadow-[0_0_20px_rgba(59,130,246,0.8)]"></div>
|
||||
<div className="absolute inset-2 border-2 border-transparent border-b-red-500 border-l-blue-400 rounded-full animate-spin-reverse shadow-[0_0_20px_rgba(239,68,68,0.8)]"></div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Data Stream */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Data Stream</h4>
|
||||
<div className="w-16 h-16 mx-auto relative overflow-hidden rounded-lg border border-blue-500/30">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-blue-500/40 to-transparent animate-dataStream"></div>
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-red-500/40 to-transparent animate-dataStream" style={{animationDelay: '0.75s'}}></div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Tron Grid Progress */}
|
||||
<Card className="p-4 bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Grid Progress</h4>
|
||||
<div className="w-full bg-black border border-blue-500/30 h-4 rounded relative overflow-hidden"
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(rgba(59, 130, 246, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(59, 130, 246, 0.1) 1px, transparent 1px)
|
||||
`,
|
||||
backgroundSize: '4px 4px'
|
||||
}}>
|
||||
<div className="h-full rounded shadow-[0_0_15px_rgba(59,130,246,0.8)] animate-progressSweep"
|
||||
style={{width: '60%'}}></div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Light Trail Skeleton */}
|
||||
<Card className="p-4 bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Light Trails</h4>
|
||||
<div className="space-y-3">
|
||||
<div className="h-3 bg-black border border-blue-500/50 rounded relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-blue-400 to-transparent w-1/3 animate-lightTrail shadow-[0_0_10px_rgba(59,130,246,0.8)]"></div>
|
||||
</div>
|
||||
<div className="h-3 bg-black border border-red-500/50 rounded w-3/4 relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-red-500 to-transparent w-1/3 animate-lightTrail shadow-[0_0_10px_rgba(239,68,68,0.8)]" style={{animationDelay: '0.5s'}}></div>
|
||||
</div>
|
||||
<div className="h-3 bg-black border border-blue-500/50 rounded w-1/2 relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-blue-400 to-transparent w-1/3 animate-lightTrail shadow-[0_0_10px_rgba(59,130,246,0.8)]" style={{animationDelay: '1s'}}></div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Energy Cascade */}
|
||||
<Card className="p-4 bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Energy Cascade</h4>
|
||||
<div className="w-full h-16 bg-black border border-blue-500/30 rounded-lg relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-blue-400/60 to-transparent animate-energyCascade shadow-[0_0_20px_rgba(59,130,246,0.6)]"></div>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-red-500/40 to-transparent animate-energyCascade shadow-[0_0_20px_rgba(239,68,68,0.4)]" style={{animationDelay: '1s'}}></div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Recognition Pattern */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Recognition Pattern</h4>
|
||||
<div className="flex gap-1 justify-center">
|
||||
{[0, 1, 2, 3, 4].map(i => (
|
||||
<div
|
||||
key={i}
|
||||
className={cn(
|
||||
"w-2 rounded-sm animate-recognitionScan",
|
||||
i % 2 === 0 ? "bg-blue-400 shadow-[0_0_8px_rgba(59,130,246,0.8)]" : "bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.8)]"
|
||||
)}
|
||||
style={{ animationDelay: `${i * 0.1}s` }}
|
||||
></div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Custom Tron Animations CSS */}
|
||||
<Card className="p-4 bg-black border border-cyan-500/30">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Tron Animation Keyframes (Add to CSS)</h4>
|
||||
<CodeDisplay
|
||||
code={`@keyframes dataStream {
|
||||
0%, 100% { transform: translateY(-100%); }
|
||||
50% { transform: translateY(100%); }
|
||||
}
|
||||
|
||||
@keyframes progressSweep {
|
||||
0%, 100% { background: linear-gradient(to right, #3b82f6, #ef4444, #3b82f6); }
|
||||
50% { background: linear-gradient(to right, #ef4444, #3b82f6, #ef4444); }
|
||||
}
|
||||
|
||||
@keyframes lightTrail {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(400%); }
|
||||
}
|
||||
|
||||
@keyframes energyCascade {
|
||||
0% { transform: translateX(-100%) skewX(12deg); }
|
||||
100% { transform: translateX(200%) skewX(12deg); }
|
||||
}
|
||||
|
||||
@keyframes recognitionScan {
|
||||
0%, 100% { height: 0.5rem; opacity: 0.3; }
|
||||
50% { height: 2rem; opacity: 1; }
|
||||
}`}
|
||||
showLineNumbers={false}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Option 3: Gradient & Neon Effects
|
||||
const renderOption3 = () => (
|
||||
<div className="space-y-8">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Zap className="w-5 h-5" />
|
||||
Gradient & Neon Effects
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{/* Multi-Color Neon Text */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Neon Typography</h4>
|
||||
<div className="space-y-2">
|
||||
<div className="text-blue-400 text-lg font-bold drop-shadow-[0_0_10px_rgba(59,130,246,0.8)]">BLUE NEON</div>
|
||||
<div className="text-pink-400 text-lg font-bold drop-shadow-[0_0_10px_rgba(244,114,182,0.8)]">PINK NEON</div>
|
||||
<div className="text-emerald-400 text-lg font-bold drop-shadow-[0_0_10px_rgba(52,211,153,0.8)]">GREEN NEON</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Neon Orb Constellation */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Neon Orbs</h4>
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<div className="w-8 h-8 bg-purple-500 rounded-full shadow-[0_0_20px_rgba(168,85,247,0.8)] animate-pulse"></div>
|
||||
<div className="w-6 h-6 bg-blue-500 rounded-full shadow-[0_0_15px_rgba(59,130,246,0.8)] animate-pulse" style={{animationDelay: '0.5s'}}></div>
|
||||
<div className="w-10 h-10 bg-pink-500 rounded-full shadow-[0_0_25px_rgba(244,114,182,0.8)] animate-pulse" style={{animationDelay: '1s'}}></div>
|
||||
<div className="w-7 h-7 bg-emerald-500 rounded-full shadow-[0_0_18px_rgba(52,211,153,0.8)] animate-pulse" style={{animationDelay: '1.5s'}}></div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Rainbow Laser Grid */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Laser Grid</h4>
|
||||
<div className="w-full h-16 relative overflow-hidden rounded-lg border border-purple-500/30">
|
||||
<div className="absolute top-2 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-blue-400 to-transparent shadow-[0_0_8px_rgba(59,130,246,0.8)]"></div>
|
||||
<div className="absolute top-6 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-pink-400 to-transparent shadow-[0_0_8px_rgba(244,114,182,0.8)]"></div>
|
||||
<div className="absolute top-10 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-emerald-400 to-transparent shadow-[0_0_8px_rgba(52,211,153,0.8)]"></div>
|
||||
<div className="absolute top-14 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-purple-400 to-transparent shadow-[0_0_8px_rgba(168,85,247,0.8)]"></div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Gradient Borders */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Neon Borders</h4>
|
||||
<div className="space-y-3">
|
||||
<div className="w-20 h-8 mx-auto bg-gradient-to-r from-purple-500 via-pink-500 to-purple-500 p-[2px] rounded-lg">
|
||||
<div className="w-full h-full bg-black rounded-md flex items-center justify-center text-xs text-gray-300">Purple</div>
|
||||
</div>
|
||||
<div className="w-20 h-8 mx-auto bg-gradient-to-r from-blue-500 via-emerald-500 to-blue-500 p-[2px] rounded-lg">
|
||||
<div className="w-full h-full bg-black rounded-md flex items-center justify-center text-xs text-gray-300">Blue</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Spectrum Wave */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Spectrum Wave</h4>
|
||||
<div className="w-full h-12 rounded-lg relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-purple-500 via-blue-500 via-emerald-500 via-pink-500 to-purple-500 animate-[spectrumWave_4s_ease-in-out_infinite] bg-[length:200%_100%]"></div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Pulsing Circuit */}
|
||||
<Card className="p-4 text-center bg-black">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Pulsing Circuit</h4>
|
||||
<div className="w-16 h-16 mx-auto relative">
|
||||
<div className="absolute inset-0 border-2 border-blue-500 rounded-lg animate-pulse shadow-[0_0_20px_rgba(59,130,246,0.6)]"></div>
|
||||
<div className="absolute inset-2 border-2 border-pink-500 rounded-lg animate-pulse shadow-[0_0_15px_rgba(244,114,182,0.6)]" style={{animationDelay: '0.5s'}}></div>
|
||||
<div className="absolute inset-4 border-2 border-emerald-500 rounded-lg animate-pulse shadow-[0_0_10px_rgba(52,211,153,0.6)]" style={{animationDelay: '1s'}}></div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Neon Animation Keyframes */}
|
||||
<Card className="p-4 bg-black border border-pink-500/30">
|
||||
<h4 className="text-sm font-medium text-gray-100 mb-3">Neon Animation Keyframes (Add to CSS)</h4>
|
||||
<CodeDisplay
|
||||
code={`@keyframes spectrumWave {
|
||||
0%, 100% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
}
|
||||
|
||||
@keyframes neonPulse {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 5px currentColor, 0 0 10px currentColor, 0 0 20px currentColor;
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 10px currentColor, 0 0 20px currentColor, 0 0 40px currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes colorShift {
|
||||
0% { filter: hue-rotate(0deg); }
|
||||
25% { filter: hue-rotate(90deg); }
|
||||
50% { filter: hue-rotate(180deg); }
|
||||
75% { filter: hue-rotate(270deg); }
|
||||
100% { filter: hue-rotate(360deg); }
|
||||
}`}
|
||||
showLineNumbers={false}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Option 4: Motion & Animation Effects
|
||||
const renderOption4 = () => (
|
||||
<div className="space-y-8">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Sparkles className="w-5 h-5" />
|
||||
Motion & Animation Effects
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{/* Fade In */}
|
||||
<Card className="p-4 text-center">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">Fade In</h4>
|
||||
<div className="w-16 h-16 mx-auto bg-gradient-to-br from-purple-500 to-emerald-500 rounded-lg animate-[fadeIn_2s_ease-in-out_infinite] shadow-[0_0_15px_rgba(168,85,247,0.4)]"></div>
|
||||
</Card>
|
||||
|
||||
{/* Slide Up */}
|
||||
<Card className="p-4 text-center">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">Slide Up</h4>
|
||||
<div className="w-16 h-16 mx-auto bg-gradient-to-br from-cyan-500 to-emerald-500 rounded-lg animate-[slideUp_2s_ease-in-out_infinite] shadow-[0_0_15px_rgba(34,211,238,0.4)]"></div>
|
||||
</Card>
|
||||
|
||||
{/* Bounce */}
|
||||
<Card className="p-4 text-center">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">Bounce</h4>
|
||||
<div className="w-16 h-16 mx-auto bg-emerald-500 rounded-lg animate-bounce shadow-[0_0_15px_rgba(16,185,129,0.6)]"></div>
|
||||
</Card>
|
||||
|
||||
{/* Wiggle */}
|
||||
<Card className="p-4 text-center">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">Wiggle</h4>
|
||||
<div className="w-16 h-16 mx-auto bg-orange-500 rounded-lg animate-[wiggle_1s_ease-in-out_infinite] shadow-[0_0_15px_rgba(249,115,22,0.4)]"></div>
|
||||
</Card>
|
||||
|
||||
{/* Float */}
|
||||
<Card className="p-4 text-center">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">Float</h4>
|
||||
<div className="w-16 h-16 mx-auto bg-pink-500 rounded-lg animate-[float_3s_ease-in-out_infinite] shadow-[0_0_15px_rgba(236,72,153,0.4)]"></div>
|
||||
</Card>
|
||||
|
||||
{/* Rotate */}
|
||||
<Card className="p-4 text-center">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">Rotate</h4>
|
||||
<div className="w-16 h-16 mx-auto bg-red-500 rounded-lg animate-spin shadow-[0_0_15px_rgba(239,68,68,0.4)]"></div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Custom Animations CSS */}
|
||||
<Card className="p-4">
|
||||
<h4 className="text-sm font-medium mb-3">Custom Animations (Add to CSS)</h4>
|
||||
<CodeDisplay
|
||||
code={`@keyframes fadeIn {
|
||||
0% { opacity: 0; transform: translateY(10px); }
|
||||
100% { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
0%, 100% { transform: translateY(20px); }
|
||||
50% { transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes wiggle {
|
||||
0%, 100% { transform: rotate(-3deg); }
|
||||
50% { transform: rotate(3deg); }
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0px); }
|
||||
50% { transform: translateY(-10px); }
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}`}
|
||||
showLineNumbers={false}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderContent = () => {
|
||||
switch (activeOption) {
|
||||
case 'Interaction':
|
||||
return renderOption1();
|
||||
case 'Loading':
|
||||
return renderOption2();
|
||||
case 'Neon':
|
||||
return renderOption3();
|
||||
case 'Animation':
|
||||
return renderOption4();
|
||||
default:
|
||||
return renderOption1();
|
||||
}
|
||||
};
|
||||
|
||||
const generateCode = () => {
|
||||
switch (activeOption) {
|
||||
case 'Interaction':
|
||||
return `// Hover & Interaction Effects
|
||||
<button className="hover:shadow-[0_0_20px_rgba(34,211,238,0.6)] transition-all duration-300">
|
||||
Glow Hover
|
||||
</button>
|
||||
|
||||
<div className="hover:scale-110 transition-transform duration-200">
|
||||
Scale Hover
|
||||
</div>
|
||||
|
||||
<div className="hover:border-cyan-500 hover:shadow-[0_0_15px_rgba(34,211,238,0.5)] transition-all duration-300">
|
||||
Neon Border Hover
|
||||
</div>
|
||||
|
||||
<div className="text-cyan-500 hover:drop-shadow-[0_0_8px_rgba(34,211,238,0.8)] transition-all duration-300">
|
||||
Glowing Text
|
||||
</div>`;
|
||||
|
||||
case 'Loading':
|
||||
return `// Tron-style Loading Effects
|
||||
<div className="w-12 h-12 border-2 border-transparent border-t-cyan-500 border-r-cyan-500 rounded-full animate-spin shadow-[0_0_15px_rgba(34,211,238,0.5)]"></div>
|
||||
|
||||
<div className="w-full bg-gray-800 border border-gray-600 rounded-full h-3 shadow-inner">
|
||||
<div className="bg-gradient-to-r from-cyan-500 to-emerald-500 h-full rounded-full shadow-[0_0_10px_rgba(34,211,238,0.6)] animate-pulse" style="width: 60%"></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 bg-gradient-to-r from-gray-700 to-gray-600 rounded animate-pulse shadow-[0_0_5px_rgba(34,211,238,0.3)]"></div>
|
||||
<div className="h-3 bg-gradient-to-r from-gray-700 to-gray-600 rounded w-3/4 animate-pulse shadow-[0_0_5px_rgba(34,211,238,0.3)]"></div>
|
||||
</div>`;
|
||||
|
||||
case 'Neon':
|
||||
return `// Gradient & Neon Effects
|
||||
<div className="bg-gradient-to-r from-purple-500 via-cyan-500 to-emerald-500 p-[2px] rounded-lg">
|
||||
<div className="bg-gray-900 rounded-md p-4">Gradient Border</div>
|
||||
</div>
|
||||
|
||||
<span className="text-cyan-400 drop-shadow-[0_0_10px_rgba(34,211,238,0.8)] font-bold">
|
||||
NEON TEXT
|
||||
</span>
|
||||
|
||||
<div className="w-12 h-12 bg-cyan-500 rounded-full shadow-[0_0_30px_rgba(34,211,238,0.8),_0_0_60px_rgba(34,211,238,0.4)] animate-pulse">
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="w-full h-16 rounded-lg border border-cyan-500/50"
|
||||
style={{
|
||||
backgroundImage: \`
|
||||
linear-gradient(rgba(34, 211, 238, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(34, 211, 238, 0.1) 1px, transparent 1px)
|
||||
\`,
|
||||
backgroundSize: '10px 10px'
|
||||
}}
|
||||
></div>`;
|
||||
|
||||
case 'Animation':
|
||||
return `// Motion & Animation Effects
|
||||
<div className="bg-gradient-to-br from-purple-500 to-emerald-500 rounded-lg animate-[fadeIn_2s_ease-in-out_infinite] shadow-[0_0_15px_rgba(168,85,247,0.4)]">
|
||||
Fade In
|
||||
</div>
|
||||
|
||||
<div className="bg-gradient-to-br from-cyan-500 to-emerald-500 rounded-lg animate-[slideUp_2s_ease-in-out_infinite] shadow-[0_0_15px_rgba(34,211,238,0.4)]">
|
||||
Slide Up
|
||||
</div>
|
||||
|
||||
<div className="bg-emerald-500 rounded-lg animate-bounce shadow-[0_0_15px_rgba(16,185,129,0.6)]">
|
||||
Bounce
|
||||
</div>
|
||||
|
||||
<div className="bg-pink-500 rounded-lg animate-[float_3s_ease-in-out_infinite] shadow-[0_0_15px_rgba(236,72,153,0.4)]">
|
||||
Float
|
||||
</div>`;
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* Header */}
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">Effects System</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-lg">
|
||||
Animation, interaction, and visual effects library
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Option Tabs */}
|
||||
<div className="flex justify-center">
|
||||
<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">
|
||||
<div className="flex gap-1 items-center">
|
||||
{EFFECT_OPTIONS.map((option) => (
|
||||
<button
|
||||
key={option}
|
||||
onClick={() => setActiveOption(option)}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-4 py-2 rounded-full transition-all duration-200",
|
||||
"text-sm font-medium whitespace-nowrap",
|
||||
activeOption === option
|
||||
? "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"
|
||||
)}
|
||||
>
|
||||
{option}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<Card className="p-8">
|
||||
{renderContent()}
|
||||
|
||||
{/* Generated Code */}
|
||||
<div className="mt-8 pt-6 border-t border-gray-200 dark:border-gray-700">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-white mb-3">Generated Code</h4>
|
||||
<CodeDisplay
|
||||
code={generateCode()}
|
||||
showLineNumbers={false}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,233 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
// Simplified spacing scale
|
||||
const SPACING_VALUES = [
|
||||
{ name: '1', pixels: '4px', value: '0.25rem' },
|
||||
{ name: '2', pixels: '8px', value: '0.5rem' },
|
||||
{ name: '3', pixels: '12px', value: '0.75rem' },
|
||||
{ name: '4', pixels: '16px', value: '1rem' },
|
||||
{ name: '6', pixels: '24px', value: '1.5rem' },
|
||||
{ name: '8', pixels: '32px', value: '2rem' },
|
||||
{ name: '12', pixels: '48px', value: '3rem' },
|
||||
{ name: '16', pixels: '64px', value: '4rem' },
|
||||
{ name: '20', pixels: '80px', value: '5rem' },
|
||||
{ name: '24', pixels: '96px', value: '6rem' }
|
||||
];
|
||||
|
||||
const SPACING_TYPES = [
|
||||
{ name: 'Padding', prefix: 'p' },
|
||||
{ name: 'Margin', prefix: 'm' },
|
||||
{ name: 'Gap', prefix: 'gap' },
|
||||
{ name: 'Space Y', prefix: 'space-y' },
|
||||
{ name: 'Space X', prefix: 'space-x' }
|
||||
];
|
||||
|
||||
export const SpacingFoundation = () => {
|
||||
const [selectedValue, setSelectedValue] = useState('4');
|
||||
const [selectedType, setSelectedType] = useState('Padding');
|
||||
|
||||
const currentSpacing = SPACING_VALUES.find(s => s.name === selectedValue);
|
||||
const currentType = SPACING_TYPES.find(t => t.name === selectedType);
|
||||
|
||||
const generateCode = () => {
|
||||
if (!currentSpacing || !currentType) return '';
|
||||
|
||||
const className = `${currentType.prefix}-${selectedValue}`;
|
||||
const pixelValue = currentSpacing.pixels;
|
||||
const remValue = currentSpacing.value;
|
||||
|
||||
return `// ${selectedType} with ${pixelValue} spacing
|
||||
<div className="${className}">
|
||||
Content with ${selectedType.toLowerCase()} of ${pixelValue}
|
||||
</div>
|
||||
|
||||
// Values:
|
||||
// ${className} = ${remValue} (${pixelValue})`;
|
||||
};
|
||||
|
||||
const renderDemo = () => {
|
||||
if (!currentSpacing || !currentType) return null;
|
||||
|
||||
const className = `${currentType.prefix}-${selectedValue}`;
|
||||
|
||||
switch (selectedType) {
|
||||
case 'Padding':
|
||||
return (
|
||||
<div className="border-2 border-dashed border-gray-300 dark:border-gray-600">
|
||||
<div className={cn(className, '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 py-4">
|
||||
Content with {currentSpacing.pixels} padding
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'Margin':
|
||||
return (
|
||||
<div className="border-2 border-dashed border-gray-300 dark:border-gray-600 p-4">
|
||||
<div className={cn(className, 'bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded text-center py-4 bg-cyan-100 dark:bg-cyan-900/30')}>
|
||||
Content with {currentSpacing.pixels} margin
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'Gap':
|
||||
return (
|
||||
<div className={cn('flex', className)}>
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="w-20 h-16 bg-cyan-100 dark:bg-cyan-900/30 border border-gray-200 dark:border-gray-700 rounded flex items-center justify-center text-sm"
|
||||
>
|
||||
Item {i}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'Space Y':
|
||||
return (
|
||||
<div className={cn('space-y-' + selectedValue)}>
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="w-full 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-sm"
|
||||
>
|
||||
Item {i}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'Space X':
|
||||
return (
|
||||
<div className={cn('flex space-x-' + selectedValue)}>
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="w-20 h-16 bg-cyan-100 dark:bg-cyan-900/30 border border-gray-200 dark:border-gray-700 rounded flex items-center justify-center text-sm"
|
||||
>
|
||||
Item {i}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* Header */}
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">Spacing System</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-lg">
|
||||
Interactive spacing scale explorer
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Spacing Explorer - Above the Fold */}
|
||||
<Card className="p-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* Left: Controls */}
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Spacing Type</label>
|
||||
<Select value={selectedType} onValueChange={setSelectedType}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{SPACING_TYPES.map((type) => (
|
||||
<SelectItem key={type.name} value={type.name}>
|
||||
{type.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Spacing Value</label>
|
||||
<Select value={selectedValue} onValueChange={setSelectedValue}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{SPACING_VALUES.map((spacing) => (
|
||||
<SelectItem key={spacing.name} value={spacing.name}>
|
||||
{spacing.name} ({spacing.pixels})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Current Selection Info */}
|
||||
<div className="p-3 bg-gray-50 dark:bg-gray-800/50 rounded-lg text-sm">
|
||||
<div className="font-medium text-gray-900 dark:text-white">
|
||||
{currentType?.prefix}-{selectedValue}
|
||||
</div>
|
||||
<div className="text-gray-600 dark:text-gray-400">
|
||||
{currentSpacing?.pixels} • {currentSpacing?.value}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Center: Live Demo */}
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="w-full max-w-xs">
|
||||
{renderDemo()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right: Spacing Scale */}
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-white">Spacing Scale</h4>
|
||||
<div className="space-y-2">
|
||||
{SPACING_VALUES.map((spacing) => (
|
||||
<div
|
||||
key={spacing.name}
|
||||
className={cn(
|
||||
"flex items-center justify-between p-2 rounded cursor-pointer transition-colors",
|
||||
selectedValue === spacing.name
|
||||
? "bg-cyan-500/20 border border-cyan-500"
|
||||
: "hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
)}
|
||||
onClick={() => setSelectedValue(spacing.name)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="bg-cyan-500 rounded"
|
||||
style={{
|
||||
width: Math.min(parseInt(spacing.pixels), 24) + 'px',
|
||||
height: '12px'
|
||||
}}
|
||||
/>
|
||||
<span className="text-sm font-mono">{spacing.name}</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500">{spacing.pixels}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Generated Code */}
|
||||
<div className="mt-8 pt-6 border-t border-gray-200 dark:border-gray-700">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-white mb-3">Generated Code</h4>
|
||||
<CodeDisplay
|
||||
code={generateCode()}
|
||||
showLineNumbers={false}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,169 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
// Typography scale
|
||||
const TYPOGRAPHY_SCALE = [
|
||||
{ name: 'Display', className: 'text-4xl md:text-5xl font-bold', example: 'Hero Title' },
|
||||
{ name: 'H1', className: 'text-3xl font-bold', example: 'Page Title' },
|
||||
{ name: 'H2', className: 'text-2xl font-bold', example: 'Section Title' },
|
||||
{ name: 'H3', className: 'text-xl font-semibold', example: 'Subsection Title' },
|
||||
{ name: 'H4', className: 'text-lg font-semibold', example: 'Component Header' },
|
||||
{ name: 'Body Large', className: 'text-lg font-normal', example: 'Large body text for important content' },
|
||||
{ name: 'Body', className: 'text-base font-normal', example: 'Standard body text for regular content' },
|
||||
{ name: 'Body Small', className: 'text-sm font-normal', example: 'Small text for descriptions' },
|
||||
{ name: 'Caption', className: 'text-xs font-normal', example: 'Caption and metadata text' },
|
||||
{ name: 'Button', className: 'text-sm font-medium', example: 'Button Label' },
|
||||
{ name: 'Code', className: 'text-sm font-mono', example: 'const variable = value;' }
|
||||
];
|
||||
|
||||
// Color variations
|
||||
const TEXT_COLORS = [
|
||||
{ name: 'Primary', className: 'text-gray-900 dark:text-gray-100' },
|
||||
{ name: 'Secondary', className: 'text-gray-700 dark:text-gray-300' },
|
||||
{ name: 'Muted', className: 'text-gray-500 dark:text-gray-400' },
|
||||
{ name: 'Accent', className: 'text-cyan-500' },
|
||||
{ name: 'Success', className: 'text-emerald-500' },
|
||||
{ name: 'Warning', className: 'text-orange-500' },
|
||||
{ name: 'Error', className: 'text-red-500' }
|
||||
];
|
||||
|
||||
export const TypographyFoundation = () => {
|
||||
const [selectedColor, setSelectedColor] = useState('Primary');
|
||||
const [selectedType, setSelectedType] = useState('H1');
|
||||
|
||||
const currentColorClass = TEXT_COLORS.find(color => color.name === selectedColor)?.className || 'text-gray-900 dark:text-gray-100';
|
||||
|
||||
const generateCode = () => {
|
||||
const selectedTypeData = TYPOGRAPHY_SCALE.find(type => type.name === selectedType);
|
||||
const selectedColorData = TEXT_COLORS.find(color => color.name === selectedColor);
|
||||
|
||||
if (!selectedTypeData || !selectedColorData) return '';
|
||||
|
||||
const className = selectedColorData.name === 'Primary'
|
||||
? selectedTypeData.className
|
||||
: `${selectedTypeData.className} ${selectedColorData.className}`;
|
||||
|
||||
return `// ${selectedTypeData.name} with ${selectedColorData.name} color
|
||||
<${selectedType.toLowerCase().startsWith('h') ? selectedType.toLowerCase() : 'p'} className="${className}">
|
||||
${selectedTypeData.example}
|
||||
</${selectedType.toLowerCase().startsWith('h') ? selectedType.toLowerCase() : 'p'}>`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* Header */}
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">Typography System</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-lg">
|
||||
Interactive typography configurator with live examples
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Typography Configurator */}
|
||||
<Card className="p-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
{/* Left: Controls */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold">Typography Configurator</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Typography Style</label>
|
||||
<Select value={selectedType} onValueChange={setSelectedType}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{TYPOGRAPHY_SCALE.map((type) => (
|
||||
<SelectItem key={type.name} value={type.name}>
|
||||
{type.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Text Color</label>
|
||||
<Select value={selectedColor} onValueChange={setSelectedColor}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{TEXT_COLORS.map((color) => (
|
||||
<SelectItem key={color.name} value={color.name}>
|
||||
{color.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Current Selection */}
|
||||
<div className="p-3 bg-gray-50 dark:bg-gray-800/50 rounded-lg">
|
||||
<div className="font-medium text-sm">Current Selection</div>
|
||||
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1">
|
||||
{selectedType} • {selectedColor}
|
||||
</div>
|
||||
<div className="text-xs font-mono text-gray-500 mt-1">
|
||||
{TYPOGRAPHY_SCALE.find(t => t.name === selectedType)?.className} {selectedColor !== 'Primary' ? TEXT_COLORS.find(c => c.name === selectedColor)?.className : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right: Live Examples */}
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-semibold">Live Examples</h3>
|
||||
|
||||
{/* Card Example */}
|
||||
<Card className="p-4">
|
||||
<div className={cn(TYPOGRAPHY_SCALE.find(t => t.name === selectedType)?.className, currentColorClass)}>
|
||||
Sample {selectedType} Text
|
||||
</div>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400 mt-2">
|
||||
This is how your typography appears in a card component
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Code Example */}
|
||||
<Card className="p-4 bg-gray-900 dark:bg-gray-950">
|
||||
<div className="text-green-400 text-xs font-mono mb-1">// Code example</div>
|
||||
<div className={cn(TYPOGRAPHY_SCALE.find(t => t.name === selectedType)?.className, currentColorClass, 'font-mono')}>
|
||||
function example() {'{'}
|
||||
</div>
|
||||
<div className="text-gray-400 text-sm font-mono ml-4">
|
||||
console.log('Typography in code context');
|
||||
</div>
|
||||
<div className={cn(TYPOGRAPHY_SCALE.find(t => t.name === selectedType)?.className, currentColorClass, 'font-mono')}>
|
||||
{'}'}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Section Example */}
|
||||
<div className="space-y-2">
|
||||
<div className={cn(TYPOGRAPHY_SCALE.find(t => t.name === selectedType)?.className, currentColorClass)}>
|
||||
Section Header Example
|
||||
</div>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">
|
||||
This demonstrates how the typography works as section headers with descriptions below.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Generated Code */}
|
||||
<div className="mt-8 pt-6 border-t border-gray-200 dark:border-gray-700">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-white mb-3">Generated Code</h4>
|
||||
<CodeDisplay
|
||||
code={generateCode()}
|
||||
showLineNumbers={false}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -3,4 +3,3 @@ export { PillNavigation } from "./shared/PillNavigation";
|
||||
export { SideNavigation } from "./shared/SideNavigation";
|
||||
export { StyleGuideTab } from "./tabs/StyleGuideTab";
|
||||
export { LayoutsTab } from "./tabs/LayoutsTab";
|
||||
export { ConfiguratorsTab } from "./tabs/ConfiguratorsTab";
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
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 blur="lg" 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>
|
||||
);
|
||||
@@ -1,17 +0,0 @@
|
||||
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>
|
||||
);
|
||||
@@ -1,58 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { Eye, Code } from 'lucide-react';
|
||||
|
||||
interface ConfiguratorCardProps {
|
||||
title: string;
|
||||
description?: string;
|
||||
code: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function ConfiguratorCard({ title, description, code, children }: ConfiguratorCardProps) {
|
||||
const [showCode, setShowCode] = useState(false);
|
||||
|
||||
return (
|
||||
<Card className="p-6" glassTint="none" glowColor="none">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">{title}</h3>
|
||||
{description && (
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">{description}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant={!showCode ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setShowCode(false)}
|
||||
>
|
||||
<Eye className="w-4 h-4 mr-2" />
|
||||
Preview
|
||||
</Button>
|
||||
<Button
|
||||
variant={showCode ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setShowCode(true)}
|
||||
>
|
||||
<Code className="w-4 h-4 mr-2" />
|
||||
Code
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showCode ? (
|
||||
<div className="rounded-lg bg-gray-900 p-4 overflow-x-auto">
|
||||
<pre className="text-xs text-gray-100">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
) : (
|
||||
<div>{children}</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -1,91 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -1,172 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Type,
|
||||
Sparkles,
|
||||
CreditCard,
|
||||
MousePointer,
|
||||
Navigation as NavIcon,
|
||||
Square,
|
||||
FormInput,
|
||||
Table as TableIcon,
|
||||
ToggleLeft,
|
||||
Power,
|
||||
CheckSquare,
|
||||
} from "lucide-react";
|
||||
import { SideNavigation, type SideNavigationSection } from "../shared/SideNavigation";
|
||||
import { TypographyFoundation } from "../foundations/TypographyFoundation";
|
||||
import { EffectsFoundation } from "../foundations/EffectsFoundation";
|
||||
import { GlassCardConfigurator } from "../configurators/GlassCardConfigurator";
|
||||
import { ButtonConfigurator } from "../configurators/ButtonConfigurator";
|
||||
import { NavigationPattern } from "../configurators/NavigationConfigurator";
|
||||
import { ModalConfigurator } from "../configurators/ModalConfigurator";
|
||||
import { FormConfigurator } from "../configurators/FormConfigurator";
|
||||
import { TableConfigurator } from "../configurators/TableConfigurator";
|
||||
import { ToggleConfigurator } from "../configurators/ToggleConfigurator";
|
||||
import { SwitchConfigurator } from "../configurators/SwitchConfigurator";
|
||||
import { CheckboxConfigurator } from "../configurators/CheckboxConfigurator";
|
||||
|
||||
export const ConfiguratorsTab = () => {
|
||||
const [activeSection, setActiveSection] = useState("typography");
|
||||
|
||||
const sections: SideNavigationSection[] = [
|
||||
{ id: "typography", label: "Typography", icon: <Type className="w-3 h-3" /> },
|
||||
{ id: "effects", label: "Effects", icon: <Sparkles className="w-3 h-3" /> },
|
||||
{ id: "glass-card", label: "Glass Card", icon: <CreditCard className="w-3 h-3" /> },
|
||||
{ id: "button", label: "Button", icon: <MousePointer className="w-3 h-3" /> },
|
||||
{ id: "navigation", label: "Navigation", icon: <NavIcon className="w-3 h-3" /> },
|
||||
{ id: "modal", label: "Modal", icon: <Square className="w-3 h-3" /> },
|
||||
{ id: "form", label: "Form", icon: <FormInput className="w-3 h-3" /> },
|
||||
{ id: "table", label: "Table", icon: <TableIcon className="w-3 h-3" /> },
|
||||
{ id: "toggle", label: "Toggle", icon: <ToggleLeft className="w-3 h-3" /> },
|
||||
{ id: "switch", label: "Switch", icon: <Power className="w-3 h-3" /> },
|
||||
{ id: "checkbox", label: "Checkbox", icon: <CheckSquare className="w-3 h-3" /> },
|
||||
];
|
||||
|
||||
// Render content based on active section
|
||||
const renderContent = () => {
|
||||
switch (activeSection) {
|
||||
case "typography":
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Typography Configurator
|
||||
</h2>
|
||||
<TypographyFoundation />
|
||||
</div>
|
||||
);
|
||||
case "effects":
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Effects Configurator
|
||||
</h2>
|
||||
<EffectsFoundation />
|
||||
</div>
|
||||
);
|
||||
case "glass-card":
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Glass Card Configurator
|
||||
</h2>
|
||||
<GlassCardConfigurator />
|
||||
</div>
|
||||
);
|
||||
case "button":
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Button Configurator
|
||||
</h2>
|
||||
<ButtonConfigurator />
|
||||
</div>
|
||||
);
|
||||
case "navigation":
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Navigation Configurator
|
||||
</h2>
|
||||
<NavigationPattern />
|
||||
</div>
|
||||
);
|
||||
case "modal":
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Modal Configurator
|
||||
</h2>
|
||||
<ModalConfigurator />
|
||||
</div>
|
||||
);
|
||||
case "form":
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Form Configurator
|
||||
</h2>
|
||||
<FormConfigurator />
|
||||
</div>
|
||||
);
|
||||
case "table":
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Table Configurator
|
||||
</h2>
|
||||
<TableConfigurator />
|
||||
</div>
|
||||
);
|
||||
case "toggle":
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Toggle Configurator
|
||||
</h2>
|
||||
<ToggleConfigurator />
|
||||
</div>
|
||||
);
|
||||
case "switch":
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Switch Configurator
|
||||
</h2>
|
||||
<SwitchConfigurator />
|
||||
</div>
|
||||
);
|
||||
case "checkbox":
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Checkbox Configurator
|
||||
</h2>
|
||||
<CheckboxConfigurator />
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
||||
Glass Card Configurator
|
||||
</h2>
|
||||
<GlassCardConfigurator />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex gap-6">
|
||||
{/* Side Navigation */}
|
||||
<SideNavigation
|
||||
sections={sections}
|
||||
activeSection={activeSection}
|
||||
onSectionClick={setActiveSection}
|
||||
/>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 max-w-5xl">{renderContent()}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user