npm package: @galaxy-dev/metadsl-adapter-mui
Description: MUI 5 adapter for MetaDSL
Latest version: 0.10.9

# @galaxy-dev/metadsl-adapter-mui

Material-UI 5 adapter for MetaDSL.

## Installation

```bash
pnpm add @galaxy-dev/metadsl-adapter-mui @mui/material @emotion/react @emotion/styled
```

## Usage

```tsx
import { DSLRenderer } from '@galaxy-dev/metadsl-runtime';
import { muiAdapter } from '@galaxy-dev/metadsl-adapter-mui';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import formDSL from './form.json';

// Optional: Create MUI theme
const theme = createTheme({
  palette: {
    primary: { main: '#1976d2' },
  },
});

function App() {
  return (
    <ThemeProvider theme={theme}>
      <DSLRenderer
        dsl={formDSL}
        adapter={muiAdapter}
        locale="zh-CN"
        onSubmit={(data) => console.log(data)}
      />
    </ThemeProvider>
  );
}
```

## Supported Components

- ✅ Input (TextField)
- ✅ Select
- ✅ Radio (RadioGroup)
- ✅ Checkbox
- ✅ Button
- ✅ Form (Box)
- ✅ Grid
- ✅ Flex (Box with flexbox)
- ✅ Tabs

## Custom Styling

You can use MUI's `sx` prop or className to customize components:

```json
{
  "type": "input",
  "name": "email",
  "label": { "en-US": "Email", "zh-CN": "邮箱" },
  "className": "custom-input",
  "props": {
    "sx": {
      "backgroundColor": "#f5f5f5",
      "borderRadius": 2
    }
  }
}
```

## Architecture

### 🚨 IMPORTANT: Adapter Component Design Principles

#### 1. DSL is Library-Agnostic

**DSL definitions are unified and contain NO library-specific properties.**

The DSL schema (ButtonDSL, FieldDSL, etc.) only defines common, universal properties that work across ALL adapters:

- ✅ `label`, `placeholder`, `helperText` - universal text fields
- ✅ `disabled`, `visible`, `readOnly` - universal states
- ✅ `variant: 'contained' | 'outlined' | 'text'` - universal button styles
- ❌ NO MUI-specific props (like `sx`, `color: 'inherit'`)
- ❌ NO Ant Design-specific props (like `danger`, `ghost`)

If you need library-specific customization, use the `props` field:

```json
{
  "type": "button",
  "label": "Submit",
  "variant": "contained",
  "props": {
    "sx": { "borderRadius": 2 }, // MUI specific
    "startIcon": "<SaveIcon />" // MUI specific
  }
}
```

#### 2. Adapters Should NOT Process i18n or Expressions

**Adapter components should NEVER process i18n or expressions themselves.**

All i18n translation and expression evaluation are handled by the runtime layer:

- **ComponentRenderer** - processes component props (label, text, title, etc.)
- **FieldRenderer** - processes field props (label, placeholder, helperText, defaultValue, etc.)

Adapter components receive **already processed props** (translated strings, evaluated booleans/numbers):

```tsx
// ❌ WRONG: Do NOT do this in adapter components
import type { ButtonDSL } from '@galaxy-dev/metadsl-core';

export interface ButtonProps extends ButtonDSL {
  // This would include I18nValue types which are NOT valid as ReactNode
}

export function Button({ label }: ButtonProps) {
  const { i18n } = useDSLContext();
  const translatedLabel = i18n.translate(label); // ❌ Don't translate here!
  return <MuiButton>{translatedLabel}</MuiButton>;
}

// ✅ CORRECT: Adapter components receive processed props
export interface ButtonProps {
  label?: string; // Already translated by ComponentRenderer
  loading?: boolean; // Already evaluated by ComponentRenderer
  disabled?: boolean; // Already evaluated by ComponentRenderer
  // ...
}

export function Button({ label, loading, disabled }: ButtonProps) {
  // Use props directly - they are already processed
  return (
    <MuiButton disabled={disabled}>{loading ? <CircularProgress size={20} /> : label}</MuiButton>
  );
}
```

**Why this matters:**

1. **Type Safety** - I18nValue (Record<string, string>) is not assignable to ReactNode
2. **Separation of Concerns** - Runtime handles logic, adapters handle presentation
3. **Performance** - Avoid duplicate processing
4. **Consistency** - Same processing logic for all adapters

**Exception:** Components that are NOT rendered through ComponentRenderer/FieldRenderer (like form containers that wrap FormRenderer) need to process their own props using `usePropsProcessor`.

## License

MIT