@galaxy-dev/metadsl-adapter-mui
v0.10.8
Published
MUI 5 adapter for MetaDSL
Downloads
3,263
Readme
@galaxy-dev/metadsl-adapter-mui
Material-UI 5 adapter for MetaDSL.
Installation
pnpm add @galaxy-dev/metadsl-adapter-mui @mui/material @emotion/react @emotion/styledUsage
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:
{
"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:
{
"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):
// ❌ 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:
- Type Safety - I18nValue (Record<string, string>) is not assignable to ReactNode
- Separation of Concerns - Runtime handles logic, adapters handle presentation
- Performance - Avoid duplicate processing
- 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
