mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-30 16:54:57 +00:00
Merge pull request #335 from johndoe6345789/codex/create-fieldtypes,-propertypanels,-and-rendernode-files-ufu1c8
Refactor rendering components into modular panels
This commit is contained in:
@@ -1,16 +1,11 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { ScrollArea } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
|
||||
import type { ComponentInstance } from '@/lib/builder-types'
|
||||
import { Separator, Button } from '@/components/ui'
|
||||
import type { ComponentInstance } from '@/lib/types/builder-types'
|
||||
import { componentCatalog } from '@/lib/component-catalog'
|
||||
import { Code, PaintBrush, Trash, Palette } from '@phosphor-icons/react'
|
||||
import { Trash } from '@phosphor-icons/react'
|
||||
import { CssClassBuilder } from '@/components/CssClassBuilder'
|
||||
import { Database, DropdownConfig } from '@/lib/database'
|
||||
import { PropertyPanels } from './components/PropertyPanels'
|
||||
|
||||
interface PropertyInspectorProps {
|
||||
component: ComponentInstance | null
|
||||
@@ -67,131 +62,14 @@ export function PropertyInspector({ component, onUpdate, onDelete, onCodeEdit }:
|
||||
<p className="text-xs text-muted-foreground">Component Properties</p>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="props" className="flex-1 flex flex-col">
|
||||
<TabsList className="w-full rounded-none border-b">
|
||||
<TabsTrigger value="props" className="flex-1">
|
||||
<PaintBrush className="mr-2" size={16} />
|
||||
Props
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="code" className="flex-1">
|
||||
<Code className="mr-2" size={16} />
|
||||
Code
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="props" className="flex-1 mt-0">
|
||||
<ScrollArea className="h-full p-4">
|
||||
<div className="space-y-4">
|
||||
{componentDef?.propSchema.map(propDef => {
|
||||
const dynamicDropdown = propDef.type === 'dynamic-select'
|
||||
? dynamicDropdowns.find(d => d.name === propDef.dynamicSource)
|
||||
: null
|
||||
|
||||
return (
|
||||
<div key={propDef.name} className="space-y-2">
|
||||
<Label className="text-xs uppercase tracking-wider">{propDef.label}</Label>
|
||||
|
||||
{propDef.name === 'className' ? (
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
value={component.props[propDef.name] || ''}
|
||||
onChange={(e) => handlePropChange(propDef.name, e.target.value)}
|
||||
className="flex-1 font-mono text-xs"
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => openCssBuilder(propDef.name)}
|
||||
>
|
||||
<Palette size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
) : propDef.type === 'string' ? (
|
||||
<Input
|
||||
value={component.props[propDef.name] || ''}
|
||||
onChange={(e) => handlePropChange(propDef.name, e.target.value)}
|
||||
/>
|
||||
) : propDef.type === 'number' ? (
|
||||
<Input
|
||||
type="number"
|
||||
value={component.props[propDef.name] || ''}
|
||||
onChange={(e) => handlePropChange(propDef.name, Number(e.target.value))}
|
||||
/>
|
||||
) : propDef.type === 'boolean' ? (
|
||||
<Select
|
||||
value={String(component.props[propDef.name] || false)}
|
||||
onValueChange={(value) => handlePropChange(propDef.name, value === 'true')}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="true">True</SelectItem>
|
||||
<SelectItem value="false">False</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : propDef.type === 'select' && propDef.options ? (
|
||||
<Select
|
||||
value={component.props[propDef.name] || propDef.defaultValue}
|
||||
onValueChange={(value) => handlePropChange(propDef.name, value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{propDef.options.map(option => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : propDef.type === 'dynamic-select' && dynamicDropdown ? (
|
||||
<Select
|
||||
value={component.props[propDef.name] || ''}
|
||||
onValueChange={(value) => handlePropChange(propDef.name, value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={`Select ${dynamicDropdown.label}`} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{dynamicDropdown.options.map(option => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : null}
|
||||
|
||||
{propDef.description && (
|
||||
<p className="text-xs text-muted-foreground">{propDef.description}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
{(!componentDef?.propSchema || componentDef.propSchema.length === 0) && (
|
||||
<p className="text-sm text-muted-foreground">This component has no configurable properties.</p>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="code" className="flex-1 mt-0">
|
||||
<div className="p-4 h-full flex flex-col items-center justify-center text-center space-y-4">
|
||||
<Code size={48} className="text-muted-foreground" />
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground mb-2">
|
||||
Add custom JavaScript code for this component
|
||||
</p>
|
||||
<Button onClick={onCodeEdit} variant="outline">
|
||||
Open Code Editor
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
<PropertyPanels
|
||||
component={component}
|
||||
componentDef={componentDef}
|
||||
dynamicDropdowns={dynamicDropdowns}
|
||||
onPropChange={handlePropChange}
|
||||
onCodeEdit={onCodeEdit}
|
||||
onOpenCssBuilder={openCssBuilder}
|
||||
/>
|
||||
|
||||
<Separator />
|
||||
|
||||
|
||||
@@ -1,32 +1,16 @@
|
||||
import type { ComponentInstance } from '@/lib/builder-types'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { Textarea } from '@/components/ui'
|
||||
import { Label } from '@/components/ui'
|
||||
import { Badge } from '@/components/ui'
|
||||
import { Card } from '@/components/ui'
|
||||
import { Switch } from '@/components/ui'
|
||||
import { Checkbox } from '@/components/ui'
|
||||
import { Separator } from '@/components/ui'
|
||||
import { Alert } from '@/components/ui'
|
||||
import { Progress } from '@/components/ui'
|
||||
import { Slider } from '@/components/ui'
|
||||
import { Avatar, AvatarFallback } from '@/components/ui'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui'
|
||||
import { IRCWebchatDeclarative } from '@/components/IRCWebchatDeclarative'
|
||||
import { NotificationSummaryCard } from '@/components/NotificationSummaryCard'
|
||||
import type React from 'react'
|
||||
import type { ComponentInstance } from '@/lib/types/builder-types'
|
||||
import type { User } from '@/lib/level-types'
|
||||
import { getDeclarativeRenderer } from '@/lib/declarative-component-renderer'
|
||||
import { RenderNode } from './components/RenderNode'
|
||||
|
||||
interface RenderComponentProps {
|
||||
component: ComponentInstance
|
||||
isSelected: boolean
|
||||
onSelect: (id: string) => void
|
||||
user?: User
|
||||
contextData?: Record<string, any>
|
||||
}
|
||||
|
||||
export function RenderComponent({ component, isSelected, onSelect, user, contextData }: RenderComponentProps) {
|
||||
export function RenderComponent({ component, isSelected, onSelect, user }: RenderComponentProps) {
|
||||
const handleClick = (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
onSelect(component.id)
|
||||
@@ -41,191 +25,19 @@ export function RenderComponent({ component, isSelected, onSelect, user, context
|
||||
isSelected={isSelected}
|
||||
onSelect={onSelect}
|
||||
user={user}
|
||||
contextData={contextData}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
||||
const wrapperClass = `relative ${isSelected ? 'ring-2 ring-accent ring-offset-2' : 'hover:ring-1 hover:ring-accent/50'} transition-all cursor-pointer`
|
||||
|
||||
const renderComponentByType = () => {
|
||||
const { type, props } = component
|
||||
const renderer = getDeclarativeRenderer()
|
||||
|
||||
if (renderer.hasComponentConfig(type)) {
|
||||
if (type === 'IRCWebchat' && user) {
|
||||
return (
|
||||
<IRCWebchatDeclarative
|
||||
user={user}
|
||||
channelName={props.channelName || 'general'}
|
||||
onClose={props.onClose}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-4 border-2 border-dashed border-accent">
|
||||
Declarative Component: {type}
|
||||
<div className="text-xs text-muted-foreground mt-2">
|
||||
This is a package-defined component
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'Container':
|
||||
return (
|
||||
<div className={props.className || 'p-4'}>
|
||||
{renderChildren()}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'Flex':
|
||||
return (
|
||||
<div className={props.className || 'flex gap-4'}>
|
||||
{renderChildren()}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'Grid':
|
||||
return (
|
||||
<div className={props.className || 'grid grid-cols-2 gap-4'}>
|
||||
{renderChildren()}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'Stack':
|
||||
return (
|
||||
<div className={props.className || 'flex flex-col gap-2'}>
|
||||
{renderChildren()}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'Card':
|
||||
return (
|
||||
<Card className={props.className || 'p-6'}>
|
||||
{renderChildren()}
|
||||
</Card>
|
||||
)
|
||||
|
||||
case 'NotificationSummary':
|
||||
return (
|
||||
<NotificationSummaryCard
|
||||
title={props.title}
|
||||
subtitle={props.subtitle}
|
||||
totalLabel={props.totalLabel}
|
||||
items={props.items}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'Button':
|
||||
return (
|
||||
<Button variant={props.variant} size={props.size}>
|
||||
{props.children || 'Button'}
|
||||
</Button>
|
||||
)
|
||||
|
||||
case 'Input':
|
||||
return (
|
||||
<Input
|
||||
placeholder={props.placeholder}
|
||||
type={props.type}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'Textarea':
|
||||
return (
|
||||
<Textarea
|
||||
placeholder={props.placeholder}
|
||||
rows={props.rows}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'Label':
|
||||
return <Label>{props.children || 'Label'}</Label>
|
||||
|
||||
case 'Heading': {
|
||||
const level = props.level || '1'
|
||||
const className = props.className
|
||||
const text = props.children || 'Heading'
|
||||
|
||||
if (level === '1') return <h1 className={className}>{text}</h1>
|
||||
if (level === '2') return <h2 className={className}>{text}</h2>
|
||||
if (level === '3') return <h3 className={className}>{text}</h3>
|
||||
if (level === '4') return <h4 className={className}>{text}</h4>
|
||||
return <h1 className={className}>{text}</h1>
|
||||
}
|
||||
|
||||
case 'Text':
|
||||
return (
|
||||
<p className={props.className}>
|
||||
{props.children || 'Text'}
|
||||
</p>
|
||||
)
|
||||
|
||||
case 'Badge':
|
||||
return (
|
||||
<Badge variant={props.variant}>
|
||||
{props.children || 'Badge'}
|
||||
</Badge>
|
||||
)
|
||||
|
||||
case 'Switch':
|
||||
return <Switch />
|
||||
|
||||
case 'Checkbox':
|
||||
return <Checkbox />
|
||||
|
||||
case 'Separator':
|
||||
return <Separator />
|
||||
|
||||
case 'Alert':
|
||||
return (
|
||||
<Alert variant={props.variant}>
|
||||
{renderChildren()}
|
||||
</Alert>
|
||||
)
|
||||
|
||||
case 'Progress':
|
||||
return <Progress value={props.value || 50} />
|
||||
|
||||
case 'Slider':
|
||||
return <Slider defaultValue={props.defaultValue || [50]} max={props.max || 100} step={props.step || 1} />
|
||||
|
||||
case 'Avatar':
|
||||
return (
|
||||
<Avatar>
|
||||
<AvatarFallback>U</AvatarFallback>
|
||||
</Avatar>
|
||||
)
|
||||
|
||||
case 'Table':
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Column 1</TableHead>
|
||||
<TableHead>Column 2</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell>Data 1</TableCell>
|
||||
<TableCell>Data 2</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
|
||||
default:
|
||||
return <div className="p-4 border-2 border-dashed">Unknown Component: {type}</div>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={wrapperClass} onClick={handleClick}>
|
||||
{renderComponentByType()}
|
||||
<RenderNode
|
||||
component={component}
|
||||
renderChildren={renderChildren}
|
||||
user={user}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { Button, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
|
||||
import type { PropDefinition } from '@/lib/types/builder-types'
|
||||
import type { DropdownConfig } from '@/lib/database'
|
||||
import { Palette } from '@phosphor-icons/react'
|
||||
|
||||
interface FieldTypesProps {
|
||||
propDef: PropDefinition
|
||||
value: any
|
||||
onChange: (value: any) => void
|
||||
dynamicDropdown?: DropdownConfig | null
|
||||
onOpenCssBuilder?: () => void
|
||||
}
|
||||
|
||||
export function FieldTypes({ propDef, value, onChange, dynamicDropdown, onOpenCssBuilder }: FieldTypesProps) {
|
||||
const renderInputByType = () => {
|
||||
if (propDef.name === 'className') {
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
value={value || ''}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="flex-1 font-mono text-xs"
|
||||
/>
|
||||
{onOpenCssBuilder && (
|
||||
<Button size="sm" variant="outline" onClick={onOpenCssBuilder}>
|
||||
<Palette size={16} />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
switch (propDef.type) {
|
||||
case 'string':
|
||||
return (
|
||||
<Input
|
||||
value={value || ''}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
)
|
||||
case 'number':
|
||||
return (
|
||||
<Input
|
||||
type="number"
|
||||
value={value || ''}
|
||||
onChange={(e) => onChange(Number(e.target.value))}
|
||||
/>
|
||||
)
|
||||
case 'boolean':
|
||||
return (
|
||||
<Select
|
||||
value={String(value || false)}
|
||||
onValueChange={(val) => onChange(val === 'true')}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="true">True</SelectItem>
|
||||
<SelectItem value="false">False</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)
|
||||
case 'select':
|
||||
return (
|
||||
<Select
|
||||
value={value || propDef.defaultValue}
|
||||
onValueChange={(val) => onChange(val)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{propDef.options?.map(option => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)
|
||||
case 'dynamic-select':
|
||||
return (
|
||||
<Select
|
||||
value={value || ''}
|
||||
onValueChange={(val) => onChange(val)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={dynamicDropdown ? `Select ${dynamicDropdown.label}` : 'Select option'} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{dynamicDropdown?.options.map(option => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs uppercase tracking-wider">{propDef.label}</Label>
|
||||
{renderInputByType()}
|
||||
{propDef.description && (
|
||||
<p className="text-xs text-muted-foreground">{propDef.description}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { ScrollArea, Tabs, TabsContent, TabsList, TabsTrigger, Button } from '@/components/ui'
|
||||
import type { ComponentDefinition, ComponentInstance } from '@/lib/types/builder-types'
|
||||
import type { DropdownConfig } from '@/lib/database'
|
||||
import { Code, PaintBrush } from '@phosphor-icons/react'
|
||||
import { FieldTypes } from './FieldTypes'
|
||||
|
||||
interface PropertyPanelsProps {
|
||||
component: ComponentInstance
|
||||
componentDef?: ComponentDefinition
|
||||
dynamicDropdowns: DropdownConfig[]
|
||||
onPropChange: (propName: string, value: any) => void
|
||||
onCodeEdit: () => void
|
||||
onOpenCssBuilder: (propName: string) => void
|
||||
}
|
||||
|
||||
export function PropertyPanels({
|
||||
component,
|
||||
componentDef,
|
||||
dynamicDropdowns,
|
||||
onPropChange,
|
||||
onCodeEdit,
|
||||
onOpenCssBuilder,
|
||||
}: PropertyPanelsProps) {
|
||||
return (
|
||||
<Tabs defaultValue="props" className="flex-1 flex flex-col">
|
||||
<TabsList className="w-full rounded-none border-b">
|
||||
<TabsTrigger value="props" className="flex-1">
|
||||
<PaintBrush className="mr-2" size={16} />
|
||||
Props
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="code" className="flex-1">
|
||||
<Code className="mr-2" size={16} />
|
||||
Code
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="props" className="flex-1 mt-0">
|
||||
<ScrollArea className="h-full p-4">
|
||||
<div className="space-y-4">
|
||||
{componentDef?.propSchema?.length ? (
|
||||
componentDef.propSchema.map(propDef => {
|
||||
const dynamicDropdown =
|
||||
propDef.type === 'dynamic-select'
|
||||
? dynamicDropdowns.find(d => d.name === propDef.dynamicSource)
|
||||
: null
|
||||
|
||||
return (
|
||||
<FieldTypes
|
||||
key={propDef.name}
|
||||
propDef={propDef}
|
||||
value={component.props[propDef.name] || ''}
|
||||
onChange={(value) => onPropChange(propDef.name, value)}
|
||||
dynamicDropdown={dynamicDropdown}
|
||||
onOpenCssBuilder={() => onOpenCssBuilder(propDef.name)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">This component has no configurable properties.</p>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="code" className="flex-1 mt-0">
|
||||
<div className="p-4 h-full flex flex-col items-center justify-center text-center space-y-4">
|
||||
<Code size={48} className="text-muted-foreground" />
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground mb-2">
|
||||
Add custom JavaScript code for this component
|
||||
</p>
|
||||
<Button onClick={onCodeEdit} variant="outline">
|
||||
Open Code Editor
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
import type React from 'react'
|
||||
import type { ComponentInstance } from '@/lib/types/builder-types'
|
||||
import { Button, Input, Textarea, Label, Badge, Card, Switch, Checkbox, Separator, Alert, Progress, Slider, Avatar, AvatarFallback, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui'
|
||||
import { IRCWebchatDeclarative } from '@/components/IRCWebchatDeclarative'
|
||||
import { NotificationSummaryCard } from '@/components/NotificationSummaryCard'
|
||||
import type { User } from '@/lib/level-types'
|
||||
import { getDeclarativeRenderer } from '@/lib/declarative-component-renderer'
|
||||
|
||||
interface RenderNodeProps {
|
||||
component: ComponentInstance
|
||||
renderChildren: () => React.ReactNode
|
||||
user?: User
|
||||
}
|
||||
|
||||
export function RenderNode({ component, renderChildren, user }: RenderNodeProps) {
|
||||
const { type, props } = component
|
||||
const renderer = getDeclarativeRenderer()
|
||||
|
||||
if (renderer.hasComponentConfig(type)) {
|
||||
if (type === 'IRCWebchat' && user) {
|
||||
return (
|
||||
<IRCWebchatDeclarative
|
||||
user={user}
|
||||
channelName={props.channelName || 'general'}
|
||||
onClose={props.onClose}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-4 border-2 border-dashed border-accent">
|
||||
Declarative Component: {type}
|
||||
<div className="text-xs text-muted-foreground mt-2">
|
||||
This is a package-defined component
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'Container':
|
||||
return (
|
||||
<div className={props.className || 'p-4'}>
|
||||
{renderChildren()}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'Flex':
|
||||
return (
|
||||
<div className={props.className || 'flex gap-4'}>
|
||||
{renderChildren()}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'Grid':
|
||||
return (
|
||||
<div className={props.className || 'grid grid-cols-2 gap-4'}>
|
||||
{renderChildren()}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'Stack':
|
||||
return (
|
||||
<div className={props.className || 'flex flex-col gap-2'}>
|
||||
{renderChildren()}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'Card':
|
||||
return (
|
||||
<Card className={props.className || 'p-6'}>
|
||||
{renderChildren()}
|
||||
</Card>
|
||||
)
|
||||
|
||||
case 'NotificationSummary':
|
||||
return (
|
||||
<NotificationSummaryCard
|
||||
title={props.title}
|
||||
subtitle={props.subtitle}
|
||||
totalLabel={props.totalLabel}
|
||||
items={props.items}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'Button':
|
||||
return (
|
||||
<Button variant={props.variant} size={props.size}>
|
||||
{props.children || 'Button'}
|
||||
</Button>
|
||||
)
|
||||
|
||||
case 'Input':
|
||||
return (
|
||||
<Input
|
||||
placeholder={props.placeholder}
|
||||
type={props.type}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'Textarea':
|
||||
return (
|
||||
<Textarea
|
||||
placeholder={props.placeholder}
|
||||
rows={props.rows}
|
||||
/>
|
||||
)
|
||||
|
||||
case 'Label':
|
||||
return <Label>{props.children || 'Label'}</Label>
|
||||
|
||||
case 'Heading': {
|
||||
const level = props.level || '1'
|
||||
const className = props.className
|
||||
const text = props.children || 'Heading'
|
||||
|
||||
if (level === '1') return <h1 className={className}>{text}</h1>
|
||||
if (level === '2') return <h2 className={className}>{text}</h2>
|
||||
if (level === '3') return <h3 className={className}>{text}</h3>
|
||||
if (level === '4') return <h4 className={className}>{text}</h4>
|
||||
return <h1 className={className}>{text}</h1>
|
||||
}
|
||||
|
||||
case 'Text':
|
||||
return (
|
||||
<p className={props.className}>
|
||||
{props.children || 'Text'}
|
||||
</p>
|
||||
)
|
||||
|
||||
case 'Badge':
|
||||
return (
|
||||
<Badge variant={props.variant}>
|
||||
{props.children || 'Badge'}
|
||||
</Badge>
|
||||
)
|
||||
|
||||
case 'Switch':
|
||||
return <Switch />
|
||||
|
||||
case 'Checkbox':
|
||||
return <Checkbox />
|
||||
|
||||
case 'Separator':
|
||||
return <Separator />
|
||||
|
||||
case 'Alert':
|
||||
return (
|
||||
<Alert variant={props.variant}>
|
||||
{renderChildren()}
|
||||
</Alert>
|
||||
)
|
||||
|
||||
case 'Progress':
|
||||
return <Progress value={props.value || 50} />
|
||||
|
||||
case 'Slider':
|
||||
return <Slider defaultValue={props.defaultValue || [50]} max={props.max || 100} step={props.step || 1} />
|
||||
|
||||
case 'Avatar':
|
||||
return (
|
||||
<Avatar>
|
||||
<AvatarFallback>U</AvatarFallback>
|
||||
</Avatar>
|
||||
)
|
||||
|
||||
case 'Table':
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Column 1</TableHead>
|
||||
<TableHead>Column 2</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell>Data 1</TableCell>
|
||||
<TableCell>Data 2</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
|
||||
default:
|
||||
return <div className="p-4 border-2 border-dashed">Unknown Component: {type}</div>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user