@matteoaliano/forest-ui 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/sync.mjs +30 -1
- package/dist/{chunk-J75HY2NM.mjs → chunk-G2ZXCZKP.mjs} +14 -14
- package/dist/chunk-G2ZXCZKP.mjs.map +1 -0
- package/dist/index.d.mts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +61 -46
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +51 -37
- package/dist/index.mjs.map +1 -1
- package/dist/theme.js +12 -12
- package/dist/theme.js.map +1 -1
- package/dist/theme.mjs +1 -1
- package/guidelines/FOREST_BE_GUIDELINES.md +1 -1
- package/guidelines/FOREST_FE_GUIDELINES.md +1 -1
- package/guidelines/FOREST_UI_GUIDELINES.md +2 -1
- package/package.json +2 -1
- package/skills/forest-agency/SKILL.md +84 -0
- package/skills/forest-agency/references/components.md +94 -0
- package/skills/forest-agency/references/patterns.md +256 -0
- package/skills/forest-external/SKILL.md +86 -0
- package/skills/forest-external/references/components.md +94 -0
- package/skills/forest-external/references/patterns.md +256 -0
- package/skills/forest-internal/SKILL.md +86 -0
- package/skills/forest-internal/references/components.md +94 -0
- package/skills/forest-internal/references/patterns.md +256 -0
- package/dist/chunk-J75HY2NM.mjs.map +0 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# Forest UI — Code Patterns & Anti-Patterns
|
|
2
|
+
|
|
3
|
+
## Code Patterns
|
|
4
|
+
|
|
5
|
+
### Form Layout
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { TextField, Button, Select, MenuItem, Checkbox } from "@matteoaliano/forest-ui";
|
|
9
|
+
import Box from "@mui/material/Box"; // Box, Stack, Grid are OK from MUI
|
|
10
|
+
|
|
11
|
+
function ContactForm() {
|
|
12
|
+
return (
|
|
13
|
+
<Box sx={{ display: "flex", flexDirection: "column", gap: 3 }}>
|
|
14
|
+
<TextField label="Name" fullWidth />
|
|
15
|
+
<TextField label="Email" type="email" fullWidth />
|
|
16
|
+
<Select label="Subject" fullWidth>
|
|
17
|
+
<MenuItem value="support">Support</MenuItem>
|
|
18
|
+
<MenuItem value="sales">Sales</MenuItem>
|
|
19
|
+
</Select>
|
|
20
|
+
<Button variant="contained" type="submit">
|
|
21
|
+
Send
|
|
22
|
+
</Button>
|
|
23
|
+
</Box>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### MultiSelect with Select All
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { useState } from "react";
|
|
32
|
+
import { MultiSelect } from "@matteoaliano/forest-ui";
|
|
33
|
+
|
|
34
|
+
const options = [
|
|
35
|
+
{ label: "Bug", value: "bug" },
|
|
36
|
+
{ label: "Enhancement", value: "enhancement" },
|
|
37
|
+
{ label: "New Feature", value: "feature" },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
function CategoryFilter() {
|
|
41
|
+
const [selected, setSelected] = useState<string[]>([]);
|
|
42
|
+
return (
|
|
43
|
+
<MultiSelect
|
|
44
|
+
label="Category"
|
|
45
|
+
options={options}
|
|
46
|
+
value={selected}
|
|
47
|
+
onChange={setSelected}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Data Table
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import {
|
|
57
|
+
Table, TableContainer, TableHead, TableBody, TableRow, TableCell,
|
|
58
|
+
} from "@matteoaliano/forest-ui";
|
|
59
|
+
|
|
60
|
+
function UsersTable({ users }) {
|
|
61
|
+
return (
|
|
62
|
+
<TableContainer>
|
|
63
|
+
<Table>
|
|
64
|
+
<TableHead>
|
|
65
|
+
<TableRow>
|
|
66
|
+
<TableCell>Name</TableCell>
|
|
67
|
+
<TableCell>Email</TableCell>
|
|
68
|
+
</TableRow>
|
|
69
|
+
</TableHead>
|
|
70
|
+
<TableBody>
|
|
71
|
+
{users.map((u) => (
|
|
72
|
+
<TableRow key={u.id}>
|
|
73
|
+
<TableCell>{u.name}</TableCell>
|
|
74
|
+
<TableCell>{u.email}</TableCell>
|
|
75
|
+
</TableRow>
|
|
76
|
+
))}
|
|
77
|
+
</TableBody>
|
|
78
|
+
</Table>
|
|
79
|
+
</TableContainer>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### DataGrid
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { DataGrid, type GridColDef } from "@matteoaliano/forest-ui";
|
|
88
|
+
|
|
89
|
+
const columns: GridColDef[] = [
|
|
90
|
+
{ field: "id", headerName: "ID", width: 70 },
|
|
91
|
+
{ field: "name", headerName: "Name", flex: 1 },
|
|
92
|
+
{ field: "email", headerName: "Email", flex: 1 },
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
function UsersGrid({ rows }) {
|
|
96
|
+
return <DataGrid rows={rows} columns={columns} />;
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Feedback Pattern
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
import { Alert, AlertTitle, Snackbar } from "@matteoaliano/forest-ui";
|
|
104
|
+
|
|
105
|
+
// Inline feedback
|
|
106
|
+
<Alert severity="error">
|
|
107
|
+
<AlertTitle>Error</AlertTitle>
|
|
108
|
+
Something went wrong.
|
|
109
|
+
</Alert>
|
|
110
|
+
|
|
111
|
+
// Toast notification
|
|
112
|
+
<Snackbar open={open} autoHideDuration={4000} onClose={handleClose}>
|
|
113
|
+
<Alert severity="success" variant="filled">Saved!</Alert>
|
|
114
|
+
</Snackbar>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Confirmation Dialog
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
import { Dialog, DialogTitle, DialogContent, DialogActions, DialogContentText, Button } from "@matteoaliano/forest-ui";
|
|
121
|
+
|
|
122
|
+
function ConfirmDialog({ open, onClose, onConfirm }) {
|
|
123
|
+
return (
|
|
124
|
+
<Dialog open={open} onClose={onClose}>
|
|
125
|
+
<DialogTitle>Confirm</DialogTitle>
|
|
126
|
+
<DialogContent>
|
|
127
|
+
<DialogContentText>Are you sure?</DialogContentText>
|
|
128
|
+
</DialogContent>
|
|
129
|
+
<DialogActions>
|
|
130
|
+
<Button variant="outlined" onClick={onClose}>Cancel</Button>
|
|
131
|
+
<Button onClick={onConfirm}>Confirm</Button>
|
|
132
|
+
</DialogActions>
|
|
133
|
+
</Dialog>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Sidebar Navigation
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
import { SidebarNav, SidebarItem } from "@matteoaliano/forest-ui";
|
|
142
|
+
import DashboardOutlined from "@mui/icons-material/DashboardOutlined";
|
|
143
|
+
import SettingsOutlined from "@mui/icons-material/SettingsOutlined";
|
|
144
|
+
|
|
145
|
+
function AppShell() {
|
|
146
|
+
const [open, setOpen] = useState(true);
|
|
147
|
+
return (
|
|
148
|
+
<SidebarNav open={open} onOpenChange={setOpen}>
|
|
149
|
+
<SidebarItem icon={<DashboardOutlined />} label="Dashboard" selected />
|
|
150
|
+
<SidebarItem icon={<SettingsOutlined />} label="Settings" />
|
|
151
|
+
</SidebarNav>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Charts
|
|
157
|
+
|
|
158
|
+
Colors are automatically assigned from the theme's 12-series palette. Override with the `colors` prop if needed.
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
import { BarChart, LineChart, PieChart, useChartColors } from "@matteoaliano/forest-ui";
|
|
162
|
+
|
|
163
|
+
// Bar chart with striped variant
|
|
164
|
+
<BarChart
|
|
165
|
+
series={[
|
|
166
|
+
{ data: [10, 20, 30], label: "Current", variant: "solid" },
|
|
167
|
+
{ data: [8, 15, 25], label: "Previous", variant: "striped" },
|
|
168
|
+
]}
|
|
169
|
+
xAxis={[{ data: ["Jan", "Feb", "Mar"], scaleType: "band" }]}
|
|
170
|
+
height={300}
|
|
171
|
+
/>
|
|
172
|
+
|
|
173
|
+
// Line chart
|
|
174
|
+
<LineChart
|
|
175
|
+
series={[{ data: [10, 20, 30], label: "Revenue" }]}
|
|
176
|
+
xAxis={[{ data: ["Jan", "Feb", "Mar"], scaleType: "band" }]}
|
|
177
|
+
height={300}
|
|
178
|
+
/>
|
|
179
|
+
|
|
180
|
+
// Pie chart
|
|
181
|
+
<PieChart
|
|
182
|
+
series={[{ data: [
|
|
183
|
+
{ id: 0, value: 40, label: "Desktop" },
|
|
184
|
+
{ id: 1, value: 30, label: "Mobile" },
|
|
185
|
+
{ id: 2, value: 30, label: "Tablet" },
|
|
186
|
+
]}]}
|
|
187
|
+
height={300}
|
|
188
|
+
/>
|
|
189
|
+
|
|
190
|
+
// Access chart colors programmatically
|
|
191
|
+
const colors = useChartColors(3);
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Storybook Controls
|
|
195
|
+
|
|
196
|
+
When adding argTypes to stories:
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
const meta: Meta<typeof Button> = {
|
|
200
|
+
component: Button,
|
|
201
|
+
argTypes: {
|
|
202
|
+
variant: {
|
|
203
|
+
control: "select",
|
|
204
|
+
options: ["contained", "outlined", "text"],
|
|
205
|
+
},
|
|
206
|
+
color: {
|
|
207
|
+
control: "select",
|
|
208
|
+
options: ["primary", "secondary", "error", "warning", "success", "info"],
|
|
209
|
+
},
|
|
210
|
+
disabled: { control: "boolean" },
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Control types:** `"select"` with `options`, `"boolean"`, `"number"`, `"text"`.
|
|
216
|
+
|
|
217
|
+
## Anti-Patterns
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
// WRONG: importing from @mui/material
|
|
221
|
+
import Button from "@mui/material/Button";
|
|
222
|
+
|
|
223
|
+
// WRONG: hardcoded colors
|
|
224
|
+
<Box sx={{ backgroundColor: "#7f56d9" }} />
|
|
225
|
+
|
|
226
|
+
// WRONG: hardcoded spacing
|
|
227
|
+
<Box sx={{ padding: "16px" }} />
|
|
228
|
+
|
|
229
|
+
// WRONG: forgetting ForestProvider
|
|
230
|
+
ReactDOM.render(<App />, root); // theme won't apply
|
|
231
|
+
|
|
232
|
+
// WRONG: using filled (default) icons
|
|
233
|
+
import CloseIcon from "@mui/icons-material/Close";
|
|
234
|
+
|
|
235
|
+
// CORRECT: always use Outlined variant
|
|
236
|
+
import CloseIcon from "@mui/icons-material/CloseOutlined";
|
|
237
|
+
|
|
238
|
+
// CORRECT: use theme tokens
|
|
239
|
+
<Box sx={{ backgroundColor: "primary.main", p: 4 }} />
|
|
240
|
+
|
|
241
|
+
// WRONG: manually building multi-select with Select + Checkbox
|
|
242
|
+
// CORRECT: use the MultiSelect component
|
|
243
|
+
<MultiSelect options={options} value={value} onChange={setValue} />
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## TypeScript Props
|
|
247
|
+
|
|
248
|
+
Every component exports its props type:
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { Button, type ButtonProps } from "@matteoaliano/forest-ui";
|
|
252
|
+
|
|
253
|
+
interface MyButtonProps extends ButtonProps {
|
|
254
|
+
analyticsId: string;
|
|
255
|
+
}
|
|
256
|
+
```
|