docs: tsx,nextjs,frontends (9 files)

This commit is contained in:
Richard Ward
2025-12-30 14:29:39 +00:00
parent ed124d3241
commit 797ef925b9
9 changed files with 562 additions and 61 deletions

View File

@@ -0,0 +1,421 @@
# FakeMUI Icons Integration Guide
This document describes how to use fakemui icons in Lua packages declaratively.
## Overview
The fakemui icon system allows Lua packages to reference icons by name without directly importing React components. Icons are resolved at render time through the component registry and icon registry.
## Architecture
### Components
1. **Icon Registry** (`frontends/nextjs/src/lib/rendering/icon-registry.ts`)
- Maps icon names to fakemui icon components
- Provides alias resolution for common icon names
- Supports case-insensitive lookups
2. **Component Registry** (`frontends/nextjs/src/lib/rendering/component-registry.ts`)
- Registers the `Icon` component for use in Lua
- Maps component type names to React components
3. **Package Icon Modules** (e.g., `packages/dashboard/seed/scripts/icons.lua`)
- Provide package-specific icon name constants
- Document available icons for each package
## Usage in Lua Packages
### Basic Icon Usage
Icons can be used in Lua by specifying the icon name in the component tree:
```lua
-- Using Icon component directly
local icon_component = {
type = "Icon",
props = {
name = "CheckCircle", -- Icon name from fakemui
size = "medium", -- small, medium, large, inherit
className = "text-green-500"
}
}
```
### Icon Sizes
Available sizes:
- `small` - 20px
- `medium` - 24px (default)
- `large` - 32px
- `inherit` - Inherits from parent
### Icon Name Resolution
The icon registry supports multiple name formats:
1. **PascalCase** (exact match): `CheckCircle`, `ChevronRight`
2. **kebab-case** (alias): `check-circle`, `chevron-right`
3. **lowercase** (alias): `checkcircle`, `chevronright`
Example:
```lua
-- All of these resolve to the same icon
{ type = "Icon", props = { name = "CheckCircle" } }
{ type = "Icon", props = { name = "check-circle" } }
{ type = "Icon", props = { name = "checkcircle" } }
```
## Package-Specific Icon Modules
Each package has an `icons.lua` module that provides constants for commonly used icons:
### Dashboard Package
```lua
local icons = require("icons")
-- Using icon constants
local stat_card = {
icon = icons.get("CHART_LINE"), -- Returns "ChartLine"
label = "Total Revenue",
value = 12500
}
```
Available icons in `dashboard/icons.lua`:
- `CHART_LINE`, `TREND_UP`, `BAR_CHART`, `PIE_CHART`
- `CHECK_CIRCLE`, `SHIELD_CHECK`, `WARNING`, `ERROR`
- `CLOCK`, `CALENDAR`, `SCHEDULE`
- And more...
### Navigation Menu Package
```lua
local icons = require("icons")
local sidebar_item = {
label = "Dashboard",
path = "/dashboard",
icon = icons.get("DASHBOARD") -- Returns "Dashboard"
}
```
Available icons in `nav_menu/icons.lua`:
- `HOME`, `DASHBOARD`, `SETTINGS`, `PROFILE`
- `SEARCH`, `NOTIFICATIONS`, `MAIL`
- `ADMIN_PANEL`, `SECURITY`, `VERIFIED_USER`
- And more...
### Data Table Package
```lua
local icons = require("icons")
local actions = {
{ id = "edit", icon = icons.get("EDIT"), label = "Edit" },
{ id = "delete", icon = icons.get("DELETE"), label = "Delete" },
{ id = "export", icon = icons.get("CSV"), label = "Export CSV" }
}
```
Available icons in `data_table/icons.lua`:
- `SORT`, `SORT_ASCENDING`, `SORT_DESCENDING`
- `FILTER`, `FILTER_CLEAR`, `FILTER_OFF`
- `EDIT`, `DELETE`, `ADD`, `REMOVE`
- `CSV`, `JSON`, `EXPORT`
- And more...
### Workflow Editor Package
```lua
local icons = require("icons")
local workflow_node = {
type = "action",
label = "Process Data",
icon = icons.get("WORKFLOW") -- Returns "Workflow"
}
```
Available icons in `workflow_editor/icons.lua`:
- `WORKFLOW`, `GIT_BRANCH`, `CALL_SPLIT`
- `PLAY`, `STOP`, `PAUSE`
- `CHECK_CIRCLE`, `ERROR`, `WARNING`
- And more...
### Form Builder Package
```lua
local icons = require("icons")
local validation_rule = {
type = "required",
message = "This field is required",
icon = icons.get("ERROR") -- Returns "CircleX"
}
```
Available icons in `form_builder/icons.lua`:
- `CHECK_CIRCLE`, `ERROR`, `WARNING`, `INFO`
- `TEXT_FIELDS`, `EMAIL`, `LOCK`, `CALENDAR`
- `VISIBILITY`, `VISIBILITY_OFF`
- `FORMAT_BOLD`, `FORMAT_ITALIC`, `INSERT_LINK`
- And more...
## Complete Examples
### Dashboard Stat Card with Icon
```lua
local icons = require("icons")
local stat_card = require("stats.card")
local revenue_card = stat_card.create({
label = "Total Revenue",
value = 12500,
icon = icons.get("CHART_LINE"),
change = "+12.5%",
positive = true,
className = "col-span-1"
})
```
### Sidebar Navigation with Icons
```lua
local icons = require("icons")
local sidebar_items = {
{
label = "Dashboard",
path = "/dashboard",
icon = icons.get("DASHBOARD")
},
{
label = "Users",
path = "/users",
icon = icons.get("USERS")
},
{
label = "Settings",
path = "/settings",
icon = icons.get("SETTINGS")
}
}
```
### Data Table with Action Icons
```lua
local icons = require("icons")
local action_column = require("columns.action")
local actions = action_column("actions", {
{ id = "edit", icon = icons.get("EDIT"), label = "Edit", handler = "edit_user" },
{ id = "delete", icon = icons.get("TRASH"), label = "Delete", handler = "delete_user", confirm = true },
{ id = "view", icon = icons.get("EYE"), label = "View", handler = "view_user" }
})
```
### Workflow Node with Icon
```lua
local icons = require("icons")
local action_node = require("nodes.action")
local process_node = action_node(
"process_data",
"Process Data",
"transform",
icons.get("WORKFLOW")
)
```
### Form Field with Validation Icons
```lua
local icons = require("icons")
local email_field = {
type = "TextField",
props = {
label = "Email",
type = "email",
required = true,
helperText = "Enter your email address",
-- Icons can be added to show validation state
startIcon = icons.get("EMAIL"),
endIcon = nil -- Will be set based on validation
}
}
```
## Available Icons
The fakemui icon library includes 390+ icons. Common categories:
### Actions
`Plus`, `Add`, `Remove`, `Trash`, `Delete`, `Copy`, `Check`, `Done`, `X`, `Edit`, `Save`
### Navigation
`ArrowUp`, `ArrowDown`, `ArrowLeft`, `ArrowRight`, `ChevronUp`, `ChevronDown`, `Home`, `Menu`
### UI Controls
`Settings`, `Search`, `Filter`, `More`, `Expand`, `Collapse`, `ZoomIn`, `ZoomOut`
### Data & Status
`CheckCircle`, `Error`, `Warning`, `Info`, `Star`, `Heart`, `Flag`
### Users & Security
`User`, `Users`, `UserCircle`, `Lock`, `Key`, `Shield`, `Verified`
### Data Table
`Sort`, `SortAscending`, `FilterList`, `Csv`, `Json`, `Pagination`
### Workflow
`Workflow`, `GitBranch`, `CallSplit`, `Play`, `Stop`, `Pause`
### Forms
`TextFields`, `Email`, `Calendar`, `Checkbox`, `Radio`, `Visibility`
For a complete list, see:
- `frontends/nextjs/src/lib/rendering/icon-registry.ts` - Icon registry with aliases
- `fakemui/icons/index.ts` - All available icons
## Best Practices
1. **Use Icon Constants**: Always use the package-specific icon modules rather than hardcoding icon names:
```lua
-- Good
local icons = require("icons")
icon = icons.get("DASHBOARD")
-- Avoid
icon = "Dashboard" -- Works but not type-safe
```
2. **Check Icon Availability**: Ensure the icon exists in fakemui before using it. All icons in the package icon modules are guaranteed to exist.
3. **Use Semantic Names**: Choose icons that match the action or concept:
- Delete actions: `Trash` or `Delete`
- Edit actions: `Edit` or `Pencil`
- Success states: `CheckCircle`
- Errors: `CircleX` or `XCircle`
4. **Consistent Sizing**: Use consistent icon sizes within the same context:
- Buttons: `medium` (24px)
- Large cards/headers: `large` (32px)
- Inline text: `small` (20px)
5. **Accessibility**: Icons should be paired with text labels for accessibility:
```lua
{
type = "Button",
children = {
{ type = "Icon", props = { name = "Delete", className = "mr-2" } },
{ type = "Typography", props = { text = "Delete" } }
}
}
```
## Type Definitions
### Lua Type Annotations
```lua
---@class IconProps
---@field name string Icon name from fakemui
---@field size? "small"|"medium"|"large"|"inherit"
---@field className? string Additional CSS classes
---@class UIComponent
---@field type "Icon"
---@field props IconProps
```
## Migration from MUI Icons
If migrating from @mui/icons-material, use the icon registry aliases:
| MUI Icon | FakeMUI Equivalent |
|----------|-------------------|
| `AccountCircle` | `AccountCircle` |
| `Dashboard` | `Dashboard` |
| `Delete` | `Delete` or `Trash` |
| `Edit` | `Edit` or `Pencil` |
| `Settings` | `Settings` or `Gear` |
| `MoreVert` | `MoreVert` or `MoreVertical` |
| `ExpandMore` | `ExpandMore` or `ChevronDown` |
| `Error` | `CircleX` or `XCircle` |
## Troubleshooting
### Icon Not Rendering
1. Check icon name spelling
2. Verify icon exists in fakemui (see `fakemui/icons/index.ts`)
3. Check browser console for warnings
### Icon Name Not Found
```
Warning: Icon "IconName" not found in @/fakemui/icons
```
Solution: Use correct icon name or add to icon registry aliases
### Icon Size Not Applied
Ensure size prop is one of: `small`, `medium`, `large`, `inherit`
```lua
-- Correct
{ type = "Icon", props = { name = "Check", size = "medium" } }
-- Incorrect
{ type = "Icon", props = { name = "Check", size = 24 } } -- Use "medium" instead
```
## Reference
### Icon Registry Functions
```typescript
// Get icon component by name
getIconComponent(iconName: string): ComponentType | undefined
// Resolve icon name (handles aliases)
resolveIconName(luaIconName: string): IconName | undefined
// Check if icon name is valid
isValidIconName(iconName: string): boolean
// Get all available icon names
getAllIconNames(): IconName[]
```
### Component Registry
The Icon component is registered in the component registry:
```typescript
import { Icon } from '@/components/atoms/display/Icon'
componentRegistry = {
// ... other components
Icon,
}
```
## Summary
The fakemui icon integration provides:
1. ✅ Declarative icon usage in Lua packages
2. ✅ Type-safe icon name constants per package
3. ✅ Flexible name resolution (PascalCase, kebab-case, lowercase)
4. ✅ 390+ icons covering common use cases
5. ✅ Consistent icon sizing and styling
6. ✅ No direct React imports needed in Lua
All icons are rendered as SVG components from the fakemui library, ensuring consistent styling and performance across the application.

View File

@@ -1,41 +1,44 @@
'use client'
import {
Box,
CircularProgress,
CircularProgressProps,
LinearProgress,
LinearProgressProps,
Typography,
} from '@mui/material'
import { LinearProgress, CircularProgress } from '@/fakemui'
import { forwardRef } from 'react'
/**
* Props for the Progress component
* @extends {LinearProgressProps} Inherits Material-UI LinearProgress props
* Wrapper around fakemui LinearProgress to maintain API compatibility
*/
export interface ProgressProps extends LinearProgressProps {
export interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {
/** Progress value (0-100) */
value?: number
/** Whether to display a percentage label next to the progress bar */
showLabel?: boolean
/** Variant of the progress bar */
variant?: 'determinate' | 'indeterminate'
/** Color of the progress bar */
color?: string
/** MUI sx prop - converted to className for compatibility */
sx?: any
}
const Progress = forwardRef<HTMLDivElement, ProgressProps>(
({ value, showLabel, sx, ...props }, ref) => {
({ value, showLabel, variant, color, sx, className, ...props }, ref) => {
// Combine className with any sx-based classes
const combinedClassName = [className, sx?.className].filter(Boolean).join(' ')
if (showLabel && value !== undefined) {
return (
<Box ref={ref} sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Box sx={{ flex: 1 }}>
<div ref={ref} className={`progress-with-label ${combinedClassName}`} style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<div style={{ flex: 1 }}>
<LinearProgress
variant="determinate"
value={value}
sx={{ height: 8, borderRadius: 1, ...sx }}
className={combinedClassName}
{...props}
/>
</Box>
<Typography variant="body2" color="text.secondary" sx={{ minWidth: 40 }}>
</div>
<span className="progress-label text-secondary" style={{ minWidth: '40px' }}>
{Math.round(value)}%
</Typography>
</Box>
</span>
</div>
)
}
@@ -43,8 +46,7 @@ const Progress = forwardRef<HTMLDivElement, ProgressProps>(
<LinearProgress
ref={ref}
value={value}
variant={value !== undefined ? 'determinate' : 'indeterminate'}
sx={{ height: 8, borderRadius: 1, ...sx }}
className={combinedClassName}
{...props}
/>
)

View File

@@ -1,24 +1,32 @@
'use client'
import { Divider, DividerProps } from '@mui/material'
import { Divider } from '@/fakemui'
import { forwardRef } from 'react'
/**
* Props for the Separator component
* @extends {DividerProps} Inherits Material-UI Divider props
* Wrapper around fakemui Divider to maintain API compatibility
*/
export interface SeparatorProps extends DividerProps {
export interface SeparatorProps extends React.HTMLAttributes<HTMLHRElement> {
/** Orientation of the separator */
orientation?: 'horizontal' | 'vertical'
/** Whether the separator is decorative (for accessibility) */
decorative?: boolean
/** MUI sx prop - converted to className for compatibility */
sx?: any
}
const Separator = forwardRef<HTMLHRElement, SeparatorProps>(
({ orientation = 'horizontal', decorative, ...props }, ref) => {
({ orientation = 'horizontal', decorative, sx, className, ...props }, ref) => {
// Combine className with any sx-based classes
const combinedClassName = [className, sx?.className].filter(Boolean).join(' ')
return (
<Divider
ref={ref}
orientation={orientation}
role={decorative ? 'presentation' : 'separator'}
className={combinedClassName}
{...props}
/>
)

View File

@@ -1,22 +1,48 @@
'use client'
import { Skeleton as MuiSkeleton } from '@mui/material'
import { type ComponentProps, forwardRef } from 'react'
type MuiSkeletonProps = ComponentProps<typeof MuiSkeleton>
import { Skeleton as FakemuiSkeleton } from '@/fakemui'
import { forwardRef } from 'react'
import type { SkeletonProps as FakemuiSkeletonProps } from '@/fakemui/fakemui/feedback/Skeleton'
/**
* Props for the Skeleton component
* @extends {MuiSkeletonProps} Inherits Material-UI Skeleton props
* Wrapper around fakemui Skeleton to maintain API compatibility
*/
export interface SkeletonProps extends MuiSkeletonProps {
/** CSS class name for custom styling */
className?: string
export interface SkeletonProps extends React.HTMLAttributes<HTMLSpanElement> {
/** Shape variant */
variant?: 'text' | 'rectangular' | 'circular' | 'rounded'
/** Animation type */
animation?: 'pulse' | 'wave' | false
/** Width of the skeleton */
width?: string | number
/** Height of the skeleton */
height?: string | number
/** MUI sx prop - converted to className for compatibility */
sx?: any
}
const Skeleton = forwardRef<HTMLSpanElement, SkeletonProps>(
({ variant = 'rounded', animation = 'wave', ...props }, ref) => {
return <MuiSkeleton ref={ref} variant={variant} animation={animation} {...props} />
({ variant = 'rounded', animation = 'wave', width, height, sx, className, ...props }, ref) => {
// Map MUI variant to fakemui variant
const fakemuiVariant = variant === 'rounded' ? 'rectangular' : variant
// Map MUI animation to fakemui animation
const fakemuiAnimation = animation === 'wave' ? 'pulse' : animation
// Combine className with any sx-based classes
const combinedClassName = [className, sx?.className].filter(Boolean).join(' ')
return (
<FakemuiSkeleton
ref={ref}
variant={fakemuiVariant}
animation={fakemuiAnimation}
width={width}
height={height}
className={combinedClassName}
{...props}
/>
)
}
)

View File

@@ -1,6 +1,6 @@
'use client'
import { Box, CircularProgress, CircularProgressProps } from '@mui/material'
import { CircularProgress } from '@/fakemui'
import { forwardRef } from 'react'
/** Spinner size options */
@@ -8,13 +8,17 @@ export type SpinnerSize = 'xs' | 'sm' | 'md' | 'lg'
/**
* Props for the Spinner component
* @extends {CircularProgressProps} Inherits Material-UI CircularProgress props
* Wrapper around fakemui CircularProgress to maintain API compatibility
*/
export interface SpinnerProps extends Omit<CircularProgressProps, 'size'> {
export interface SpinnerProps extends React.HTMLAttributes<HTMLDivElement> {
/** Size of the spinner (xs: 16px, sm: 20px, md: 24px, lg: 40px) or a custom number */
size?: SpinnerSize | number
/** Whether to center the spinner in its container */
centered?: boolean
/** Color of the spinner */
color?: string
/** MUI sx prop - converted to className for compatibility */
sx?: any
}
const sizeMap: Record<SpinnerSize, number> = {
@@ -25,16 +29,26 @@ const sizeMap: Record<SpinnerSize, number> = {
}
const Spinner = forwardRef<HTMLDivElement, SpinnerProps>(
({ size = 'md', centered, ...props }, ref) => {
({ size = 'md', centered, color, sx, className, ...props }, ref) => {
const dimension = typeof size === 'number' ? size : sizeMap[size]
const spinner = <CircularProgress ref={ref} size={dimension} {...props} />
// Combine className with any sx-based classes
const combinedClassName = [className, sx?.className].filter(Boolean).join(' ')
const spinner = (
<CircularProgress
ref={ref}
size={dimension}
className={combinedClassName}
{...props}
/>
)
if (centered) {
return (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', p: 2 }}>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', padding: '1rem' }}>
{spinner}
</Box>
</div>
)
}

View File

@@ -1,12 +1,11 @@
'use client'
import { Tooltip as MuiTooltip } from '@mui/material'
import { type ComponentProps, forwardRef, ReactElement, ReactNode } from 'react'
type MuiTooltipProps = ComponentProps<typeof MuiTooltip>
import { Tooltip as FakemuiTooltip } from '@/fakemui'
import { forwardRef, ReactElement, ReactNode } from 'react'
/**
* Props for the Tooltip component
* Wrapper around fakemui Tooltip to maintain API compatibility
*/
export interface TooltipProps {
/** The element that triggers the tooltip */
@@ -27,13 +26,10 @@ export interface TooltipProps {
onOpen?: () => void
/** Callback when tooltip is closed */
onClose?: () => void
}
const sideMap: Record<string, MuiTooltipProps['placement']> = {
top: 'top',
right: 'right',
bottom: 'bottom',
left: 'left',
/** MUI placement prop (mapped to side) */
placement?: 'top' | 'right' | 'bottom' | 'left'
/** MUI enterDelay prop (mapped to delayDuration) */
enterDelay?: number
}
const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
@@ -42,8 +38,10 @@ const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
children,
content,
title,
side = 'top',
delayDuration = 300,
side,
placement,
delayDuration,
enterDelay,
arrow = true,
open,
onOpen,
@@ -53,17 +51,15 @@ const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
ref
) => {
return (
<MuiTooltip
<FakemuiTooltip
title={content || title || ''}
placement={sideMap[side]}
enterDelay={delayDuration}
placement={side || placement || 'top'}
arrow={arrow}
open={open}
onOpen={onOpen}
onClose={onClose}
{...props}
>
{children}
</MuiTooltip>
</FakemuiTooltip>
)
}
)

View File

@@ -5,6 +5,7 @@
---@field pattern? string
---@field value? number | string
---@field message string
---@field icon? string Icon name from fakemui icons for validation feedback
---@param message? string
---@return ValidationRule

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,32 @@
{
"packageId": "stats_grid",
"name": "Stats Grid",
"version": "1.0.0",
"description": "Configurable statistics grid display for dashboards and monitoring",
"icon": "static_content/icon.svg",
"author": "MetaBuilder",
"category": "ui",
"dependencies": [],
"devDependencies": ["lua_test"],
"exports": {
"components": [
"StatsGrid",
"StatCard"
],
"scripts": [
"stats",
"formatters"
]
},
"tests": {
"scripts": [
"tests/stats.test.lua",
"tests/formatters.test.lua"
],
"cases": [
"tests/stats.cases.json",
"tests/formatters.cases.json"
]
},
"minLevel": 2
}