code: tsx,storybook,src (1 files)

This commit is contained in:
Richard Ward
2025-12-30 20:49:47 +00:00
parent 3d41ecad70
commit 52d2c09a6e

View File

@@ -0,0 +1,345 @@
/**
* Component Registry
* Maps Lua component type names to React components
*/
import React, { type ComponentType, type ReactNode } from 'react'
export interface LuaComponentProps {
className?: string
children?: ReactNode
[key: string]: unknown
}
type AnyComponent = ComponentType<LuaComponentProps>
/**
* Basic UI Components
* These mirror the components available in the main app's fakemui library
*/
export const Box: React.FC<LuaComponentProps> = ({ className, children, ...props }) => (
<div className={className} {...props}>{children}</div>
)
export const Stack: React.FC<LuaComponentProps> = ({ className = 'flex flex-col gap-4', children }) => (
<div className={className}>{children}</div>
)
export const Flex: React.FC<LuaComponentProps> = ({ className = 'flex gap-4', children }) => (
<div className={className}>{children}</div>
)
export const Grid: React.FC<LuaComponentProps> = ({ className = 'grid grid-cols-2 gap-4', children }) => (
<div className={className}>{children}</div>
)
export const Container: React.FC<LuaComponentProps> = ({ className = 'max-w-7xl mx-auto px-4', children }) => (
<div className={className}>{children}</div>
)
export const Card: React.FC<LuaComponentProps> = ({ className = 'rounded-lg border shadow-sm bg-canvas', children }) => (
<div className={className}>{children}</div>
)
export const CardHeader: React.FC<LuaComponentProps> = ({ className = 'p-6 pb-2', children }) => (
<div className={className}>{children}</div>
)
export const CardContent: React.FC<LuaComponentProps> = ({ className = 'p-6 pt-0', children }) => (
<div className={className}>{children}</div>
)
export const CardActions: React.FC<LuaComponentProps> = ({ className = 'p-6 pt-0 flex gap-2', children }) => (
<div className={className}>{children}</div>
)
export const Paper: React.FC<LuaComponentProps> = ({ className = 'rounded border p-4 bg-canvas', children }) => (
<div className={className}>{children}</div>
)
interface TypographyProps extends LuaComponentProps {
variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'body1' | 'body2' | 'caption' | 'overline'
text?: string
}
export const Typography: React.FC<TypographyProps> = ({
variant = 'body1',
text,
className = '',
children
}) => {
const content = text ?? children
const variantClasses: Record<string, string> = {
h1: 'text-4xl font-bold',
h2: 'text-3xl font-bold',
h3: 'text-2xl font-semibold',
h4: 'text-xl font-semibold',
h5: 'text-lg font-medium',
h6: 'text-base font-medium',
body1: 'text-base',
body2: 'text-sm',
caption: 'text-xs text-muted-foreground',
overline: 'text-xs uppercase tracking-wide text-muted-foreground',
}
const Element = variant.startsWith('h') ? variant as keyof JSX.IntrinsicElements : 'p'
return <Element className={`${variantClasses[variant]} ${className}`}>{content}</Element>
}
interface ButtonProps extends LuaComponentProps {
variant?: 'contained' | 'outlined' | 'text'
color?: 'primary' | 'secondary' | 'error'
size?: 'small' | 'medium' | 'large'
onClick?: () => void
}
export const Button: React.FC<ButtonProps> = ({
variant = 'contained',
color = 'primary',
size = 'medium',
className = '',
children,
onClick,
}) => {
const baseClass = 'rounded font-medium transition-colors cursor-pointer'
const sizeClasses = {
small: 'px-2 py-1 text-sm',
medium: 'px-4 py-2',
large: 'px-6 py-3 text-lg',
}
const variantClasses = {
contained: 'bg-accent text-accent-foreground hover:opacity-90',
outlined: 'border border-accent text-accent hover:bg-accent/10',
text: 'text-accent hover:bg-accent/10',
}
return (
<button
className={`${baseClass} ${sizeClasses[size]} ${variantClasses[variant]} ${className}`}
onClick={onClick}
style={{ backgroundColor: variant === 'contained' ? 'var(--color-accent)' : undefined }}
>
{children}
</button>
)
}
interface IconProps extends LuaComponentProps {
name: string
size?: 'small' | 'medium' | 'large'
}
export const Icon: React.FC<IconProps> = ({ name, size = 'medium', className = '' }) => {
const sizeClasses = { small: 'text-sm', medium: 'text-xl', large: 'text-3xl' }
// Simple emoji/text fallback for icons
const iconMap: Record<string, string> = {
users: '👥',
settings: '⚙️',
dashboard: '📊',
home: '🏠',
edit: '✏️',
delete: '🗑️',
add: '',
check: '✓',
close: '✕',
arrow_up: '↑',
arrow_down: '↓',
trending_up: '📈',
trending_down: '📉',
}
return <span className={`${sizeClasses[size]} ${className}`}>{iconMap[name] || `[${name}]`}</span>
}
export const Divider: React.FC<LuaComponentProps> = ({ className = 'border-t my-4' }) => (
<hr className={className} />
)
export const Avatar: React.FC<LuaComponentProps & { src?: string; alt?: string }> = ({
src,
alt = 'Avatar',
className = 'w-10 h-10 rounded-full bg-muted flex items-center justify-center'
}) => (
src ? <img src={src} alt={alt} className={className} /> : <div className={className}>{alt[0]}</div>
)
interface TabsProps extends LuaComponentProps {
value?: string
items?: Array<{ value: string; label: string; content?: ReactNode }>
}
export const Tabs: React.FC<TabsProps> = ({ items = [], className = '' }) => {
const [active, setActive] = React.useState(items[0]?.value || '')
return (
<div className={className}>
<div className="flex border-b gap-4">
{items.map(item => (
<button
key={item.value}
className={`py-2 px-1 border-b-2 transition-colors ${
active === item.value
? 'border-accent text-accent'
: 'border-transparent text-muted-foreground hover:text-foreground'
}`}
onClick={() => setActive(item.value)}
>
{item.label}
</button>
))}
</div>
<div className="py-4">
{items.find(i => i.value === active)?.content}
</div>
</div>
)
}
export const Tab: React.FC<LuaComponentProps & { label?: string; value?: string }> = ({
label,
children,
className = 'py-2 px-4'
}) => (
<div className={className}>{label || children}</div>
)
export const Alert: React.FC<LuaComponentProps & { severity?: 'info' | 'success' | 'warning' | 'error' }> = ({
severity = 'info',
className = '',
children,
}) => {
const colors = {
info: 'bg-blue-50 border-blue-200 text-blue-800',
success: 'bg-green-50 border-green-200 text-green-800',
warning: 'bg-yellow-50 border-yellow-200 text-yellow-800',
error: 'bg-red-50 border-red-200 text-red-800',
}
return <div className={`p-4 rounded border ${colors[severity]} ${className}`}>{children}</div>
}
export const Badge: React.FC<LuaComponentProps & { color?: string }> = ({
color = 'default',
className = '',
children,
}) => (
<span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-muted ${className}`}>
{children}
</span>
)
export const Chip: React.FC<LuaComponentProps & { label?: string }> = ({
label,
className = 'inline-flex items-center px-3 py-1 rounded-full text-sm bg-muted',
children,
}) => (
<span className={className}>{label || children}</span>
)
// Placeholder components for complex Lua package components
export const Level4Header: React.FC<LuaComponentProps & { username?: string; nerdMode?: boolean }> = ({
username = 'User',
nerdMode = false,
}) => (
<header className="border-b p-4 flex items-center justify-between bg-canvas">
<div className="flex items-center gap-4">
<Typography variant="h5">Level 4 - Admin Panel</Typography>
{nerdMode && <Badge>Nerd Mode</Badge>}
</div>
<div className="flex items-center gap-2">
<Avatar alt={username} />
<span>{username}</span>
</div>
</header>
)
export const IntroSection: React.FC<LuaComponentProps & {
eyebrow?: string
title?: string
description?: string
}> = ({ eyebrow, title, description }) => (
<section className="space-y-4">
{eyebrow && <Typography variant="overline">{eyebrow}</Typography>}
{title && <Typography variant="h2">{title}</Typography>}
{description && <Typography variant="body1" className="text-muted-foreground">{description}</Typography>}
</section>
)
export const AppHeader: React.FC<LuaComponentProps> = ({ children }) => (
<header className="border-b p-4 bg-canvas">{children}</header>
)
export const AppFooter: React.FC<LuaComponentProps> = ({ children }) => (
<footer className="border-t p-4 bg-canvas mt-auto">{children}</footer>
)
export const Sidebar: React.FC<LuaComponentProps> = ({ children, className = 'w-64 border-r p-4 bg-canvas' }) => (
<aside className={className}>{children}</aside>
)
/**
* Component Registry - maps Lua type names to React components
*/
export const componentRegistry: Record<string, AnyComponent> = {
// Layout
Box,
Stack,
Flex,
Grid,
Container,
// Surfaces
Card,
CardHeader,
CardContent,
CardActions,
CardTitle: CardHeader,
CardFooter: CardActions,
Paper,
// Typography
Typography,
Text: Typography,
// Inputs
Button,
// Display
Icon,
Avatar,
Badge,
Chip,
Divider,
Alert,
// Navigation
Tabs,
Tab,
// App-specific (from Lua packages)
Level4Header,
IntroSection,
AppHeader,
AppFooter,
Sidebar,
}
/**
* Get a component by its Lua type name
*/
export function getComponent(typeName: string): AnyComponent | undefined {
return componentRegistry[typeName]
}
/**
* Register a custom component
*/
export function registerComponent(typeName: string, component: AnyComponent): void {
componentRegistry[typeName] = component
}
/**
* Check if a component type is registered
*/
export function hasComponent(typeName: string): boolean {
return typeName in componentRegistry
}