@maxio-com/react-ui-components 9.16.0 → 9.16.1
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/dist/skills/maxio-react/SKILL.md +68 -0
- package/dist/skills/maxio-react/agents/openai.yaml +4 -0
- package/dist/skills/maxio-react/references/components-action-list.md +92 -0
- package/dist/skills/maxio-react/references/components-action-menu.md +162 -0
- package/dist/skills/maxio-react/references/components-avatar.md +66 -0
- package/dist/skills/maxio-react/references/components-breadcrumbs.md +86 -0
- package/dist/skills/maxio-react/references/components-buttons-button.md +170 -0
- package/dist/skills/maxio-react/references/components-buttons-iconbutton.md +102 -0
- package/dist/skills/maxio-react/references/components-card.md +175 -0
- package/dist/skills/maxio-react/references/components-chip.md +151 -0
- package/dist/skills/maxio-react/references/components-data-table.md +344 -0
- package/dist/skills/maxio-react/references/components-drawer.md +293 -0
- package/dist/skills/maxio-react/references/components-flex.md +185 -0
- package/dist/skills/maxio-react/references/components-forms-checkbox.md +257 -0
- package/dist/skills/maxio-react/references/components-forms-combobox.md +250 -0
- package/dist/skills/maxio-react/references/components-forms-radio-group.md +202 -0
- package/dist/skills/maxio-react/references/components-forms-select.md +295 -0
- package/dist/skills/maxio-react/references/components-forms-textfield.md +253 -0
- package/dist/skills/maxio-react/references/components-grid.md +140 -0
- package/dist/skills/maxio-react/references/components-icon.md +169 -0
- package/dist/skills/maxio-react/references/components-link.md +180 -0
- package/dist/skills/maxio-react/references/components-listbox.md +205 -0
- package/dist/skills/maxio-react/references/components-loading-spinner.md +123 -0
- package/dist/skills/maxio-react/references/components-logo.md +91 -0
- package/dist/skills/maxio-react/references/components-notifications-alert.md +103 -0
- package/dist/skills/maxio-react/references/components-notifications-banner.md +173 -0
- package/dist/skills/maxio-react/references/components-notifications-toast.md +155 -0
- package/dist/skills/maxio-react/references/components-pagination.md +115 -0
- package/dist/skills/maxio-react/references/components-popover.md +176 -0
- package/dist/skills/maxio-react/references/components-progressbar.md +98 -0
- package/dist/skills/maxio-react/references/components-segmentedcontrol.md +292 -0
- package/dist/skills/maxio-react/references/components-tabs.md +193 -0
- package/dist/skills/maxio-react/references/components-tag.md +117 -0
- package/dist/skills/maxio-react/references/components-tile.md +114 -0
- package/dist/skills/maxio-react/references/components-toggle.md +218 -0
- package/dist/skills/maxio-react/references/components-tooltip.md +178 -0
- package/dist/skills/maxio-react/references/components-typography-body.md +101 -0
- package/dist/skills/maxio-react/references/components-typography-code.md +72 -0
- package/dist/skills/maxio-react/references/components-typography-display.md +91 -0
- package/dist/skills/maxio-react/references/components-typography-heading.md +88 -0
- package/dist/skills/maxio-react/references/components-typography-label.md +82 -0
- package/dist/skills/maxio-react/references/deprecated-textinput.md +250 -0
- package/dist/skills/maxio-react/references/foundations-dark-mode.md +85 -0
- package/dist/skills/maxio-react/references/foundations-design-tokens-about.md +108 -0
- package/dist/skills/maxio-react/references/foundations-design-tokens-colors-primitive-colors.md +33 -0
- package/dist/skills/maxio-react/references/foundations-design-tokens-colors-semantic-colors.md +11 -0
- package/dist/skills/maxio-react/references/foundations-start-here.md +128 -0
- package/dist/skills/maxio-react/references/patterns-auth-layout.md +318 -0
- package/dist/skills/maxio-react/references/patterns-side-nav.md +182 -0
- package/dist/skills/maxio-react/references/patterns-top-bar.md +214 -0
- package/package.json +2 -2
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# DataTable
|
|
2
|
+
|
|
3
|
+
## Usage Guidelines
|
|
4
|
+
|
|
5
|
+
### Overview
|
|
6
|
+
|
|
7
|
+
Displays TanStack Table data with Maxio table semantics, styling, and common interactions such as sorting, row selection, expansion, row actions, and pinned columns.
|
|
8
|
+
|
|
9
|
+
DataTable uses TanStack Table for column, row, sorting, selection, expansion,
|
|
10
|
+
and pinning state while rendering Maxio table markup and classes.
|
|
11
|
+
|
|
12
|
+
#### When to Use
|
|
13
|
+
|
|
14
|
+
- Use DataTable for tabular customer, invoice, subscription, or finance data.
|
|
15
|
+
- Use DataTable when rows need sorting, selection, expansion, row actions, or pinned columns.
|
|
16
|
+
- Use DataTable when a table needs consistent Maxio styling and accessible table semantics.
|
|
17
|
+
- Use DataTable when the product surface already defines data and columns with TanStack Table.
|
|
18
|
+
|
|
19
|
+
#### When Not to Use
|
|
20
|
+
|
|
21
|
+
- Do not use DataTable for simple key-value summaries or static comparison tables. Use primitive table components or layout primitives instead.
|
|
22
|
+
- Do not use DataTable for non-tabular card, list, or navigation layouts.
|
|
23
|
+
- Do not hand-code sorting, selection, expansion, or pinning behavior outside TanStack Table.
|
|
24
|
+
|
|
25
|
+
### Configuration
|
|
26
|
+
|
|
27
|
+
DataTable renders the Maxio table shell around TanStack data and column
|
|
28
|
+
definitions. Configure cell content, alignment, widths, grouped headers,
|
|
29
|
+
sorting behavior, and pinned columns through TanStack column definitions and
|
|
30
|
+
table state.
|
|
31
|
+
|
|
32
|
+
| Option | Purpose | Usage notes |
|
|
33
|
+
| :-------------------- | :------------------------------------------------------- | :------------------------------------------------------------------------------ |
|
|
34
|
+
| `enableSorting` | Allows sortable columns to cycle sort state. | Configure per-column sorting behavior in the TanStack column definition. |
|
|
35
|
+
| `enableRowSelection` | Adds checkbox controls for selecting rows. | Use controlled row selection state when selections drive actions outside table. |
|
|
36
|
+
| `enableExpanding` | Adds row expansion controls. | Use `rowsOnly` when the table should not show an expand-all header control. |
|
|
37
|
+
| `actions` | Adds a pinned row actions cell. | Use `IconButton` size `sm` for icon-only row actions. |
|
|
38
|
+
| `onRowAction` | Makes each parent row activate a primary row action. | Keep nested controls interactive without triggering the row action. |
|
|
39
|
+
| `SubRowComponent` | Renders custom expanded content connected to parent row. | Keep expanded content concise and related to the row. |
|
|
40
|
+
| `state.columnPinning` | Pins important columns to the left or right. | Set numeric column `size` values when multiple columns are pinned on one side. |
|
|
41
|
+
|
|
42
|
+
### Behavior
|
|
43
|
+
|
|
44
|
+
- **Mouse and touch**: header controls sort columns, row checkboxes select rows, expand controls show nested or custom row content, and row action controls activate row-specific commands.
|
|
45
|
+
- **Keyboard**: interactive controls are reachable with Tab. Buttons and checkboxes keep their native keyboard behavior. Actionable rows receive keyboard focus.
|
|
46
|
+
- **Focus management**: keep focus indicators visible on row controls, sortable headers, action buttons, links, and actionable rows.
|
|
47
|
+
- **Controlled state**: use TanStack Table state props when sorting, selection, expansion, or column pinning must be controlled by the product surface.
|
|
48
|
+
- **Column pinning**: set numeric `size` values for multiple pinned columns on the same side so TanStack can calculate sticky offsets correctly.
|
|
49
|
+
|
|
50
|
+
### Accessibility
|
|
51
|
+
|
|
52
|
+
- Use semantic `<table>`, `<thead>`, `<tbody>`, `<tr>`, `<th>`, and `<td>` markup.
|
|
53
|
+
- Keep sortable header state synchronized with `aria-sort`.
|
|
54
|
+
- Give selection, expansion, and row action controls clear accessible names.
|
|
55
|
+
- Label actionable rows from visible row content with `aria-labelledby`.
|
|
56
|
+
- Preserve table semantics when rendering links, tags, buttons, or custom content inside cells.
|
|
57
|
+
- Do not rely on color alone to communicate sorted, selected, expanded, or actionable state.
|
|
58
|
+
|
|
59
|
+
### Content
|
|
60
|
+
|
|
61
|
+
- Use concise column headers that match the data users need to compare.
|
|
62
|
+
- Put units in headers when they apply to the whole column, such as "Home Revenue (USD)".
|
|
63
|
+
- Keep row action labels specific to the command or provide a descriptive `aria-label` for icon-only actions.
|
|
64
|
+
- Prefer scannable cell values over long prose. Use expandable custom rows for secondary detail.
|
|
65
|
+
|
|
66
|
+
### Related
|
|
67
|
+
|
|
68
|
+
- **[Link](components-link.md)**: use for plain text navigation inside table cells.
|
|
69
|
+
- **[IconButton](components-buttons-iconbutton.md)**: use for icon-only row actions.
|
|
70
|
+
- **[Checkbox](components-forms-checkbox.md)**: use for custom selection controls outside DataTable.
|
|
71
|
+
|
|
72
|
+
## React
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { DataTable } from '@maxio-com/react-ui-components';
|
|
76
|
+
import { createColumnHelper } from '@tanstack/react-table';
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Column Configuration
|
|
80
|
+
|
|
81
|
+
Define table data and columns with TanStack Table, then pass those columns and
|
|
82
|
+
rows to DataTable.
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
type Customer = {
|
|
86
|
+
name: string;
|
|
87
|
+
accountNumber: string;
|
|
88
|
+
homeRevenue: number;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const columnHelper = createColumnHelper<Customer>();
|
|
92
|
+
|
|
93
|
+
const columns = [
|
|
94
|
+
columnHelper.accessor('name', {
|
|
95
|
+
header: 'Name',
|
|
96
|
+
cell: (info) => info.getValue(),
|
|
97
|
+
}),
|
|
98
|
+
columnHelper.accessor('accountNumber', {
|
|
99
|
+
header: 'Account #',
|
|
100
|
+
cell: (info) => info.getValue(),
|
|
101
|
+
}),
|
|
102
|
+
columnHelper.accessor('homeRevenue', {
|
|
103
|
+
header: 'Home Revenue (USD)',
|
|
104
|
+
cell: (info) => info.getValue(),
|
|
105
|
+
}),
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
<DataTable columns={columns} data={customers} />;
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Use the column `meta` field for Maxio-specific presentation details.
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
columnHelper.accessor('homeRevenue', {
|
|
115
|
+
header: 'Home Revenue (USD)',
|
|
116
|
+
cell: (info) => info.getValue(),
|
|
117
|
+
meta: {
|
|
118
|
+
align: 'right',
|
|
119
|
+
width: '12rem',
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### State Management
|
|
125
|
+
|
|
126
|
+
DataTable can let TanStack Table manage interaction state internally. Control
|
|
127
|
+
state when sorting, selection, expansion, or pinned columns need to drive
|
|
128
|
+
application behavior outside the table.
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
<DataTable
|
|
132
|
+
columns={columns}
|
|
133
|
+
data={customers}
|
|
134
|
+
enableSorting
|
|
135
|
+
enableRowSelection
|
|
136
|
+
state={{
|
|
137
|
+
columnPinning: {
|
|
138
|
+
left: ['name', 'accountNumber'],
|
|
139
|
+
right: ['homeRevenue'],
|
|
140
|
+
},
|
|
141
|
+
}}
|
|
142
|
+
onSort={setSorting}
|
|
143
|
+
onSelect={setRowSelection}
|
|
144
|
+
onExpand={setExpanded}
|
|
145
|
+
/>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Expansion
|
|
149
|
+
|
|
150
|
+
Use nested `subRows` data for child records with the same shape as their parent
|
|
151
|
+
row. Use `SubRowComponent` when expanded content needs a custom layout that is
|
|
152
|
+
still attached to the row.
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
<DataTable
|
|
156
|
+
columns={columns}
|
|
157
|
+
data={customers}
|
|
158
|
+
enableExpanding="rowsOnly"
|
|
159
|
+
SubRowComponent={({ original }) => <CustomerDetails customer={original} />}
|
|
160
|
+
/>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Imports
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
import {
|
|
167
|
+
DataTable,
|
|
168
|
+
HeaderCell,
|
|
169
|
+
Icon,
|
|
170
|
+
IconButton,
|
|
171
|
+
Link,
|
|
172
|
+
Table,
|
|
173
|
+
TableBody,
|
|
174
|
+
TableCell,
|
|
175
|
+
TableHead,
|
|
176
|
+
TableRow,
|
|
177
|
+
} from "@maxio-com/react-ui-components";
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Prop Types
|
|
181
|
+
|
|
182
|
+
### DataTable
|
|
183
|
+
|
|
184
|
+
| Prop | Type | Required | Default | Description | Source |
|
|
185
|
+
| --- | --- | --- | --- | --- | --- |
|
|
186
|
+
| `actions` | `((row: Row<TData>, table: Table<TData>) => ReactNode)` | no | - | render custom actions cell content | TypeLiteral |
|
|
187
|
+
| `actionsColumnSize` | `number` | no | - | width (in px) of the actions column; defaults to 40 | TypeLiteral |
|
|
188
|
+
| `aggregationFns` | `Record<string, AggregationFn<any>>` | no | - | - | TypeLiteral |
|
|
189
|
+
| `align` | `"left" \| "right"` | no | - | - | TypeLiteral |
|
|
190
|
+
| `enableExpanding` | `boolean \| "rowsOnly"` | no | - | Enable nested row expansion. Include `'rowsOnly'` for row-level toggles without group expand/collapse header control. | TypeLiteral |
|
|
191
|
+
| `filterFns` | `Record<string, FilterFn<any>>` | no | - | - | TypeLiteral |
|
|
192
|
+
| `isLoading` | `boolean` | no | - | - | TypeLiteral |
|
|
193
|
+
| `onExpand` | `((expanded: ExpandedState) => void)` | no | - | function to peek into expanding state changes | TypeLiteral |
|
|
194
|
+
| `onRowAction` | `((row: Row<TData>) => void)` | no | - | function called with the pressed row | TypeLiteral |
|
|
195
|
+
| `onSelect` | `((selected: RowSelectionState) => void)` | no | - | function to peek into selection state changes | TypeLiteral |
|
|
196
|
+
| `onSort` | `((selected: SortingState) => void)` | no | - | function to peek into sorting state changes | TypeLiteral |
|
|
197
|
+
| `pattern` | `boolean` | no | - | - | TypeLiteral |
|
|
198
|
+
| `size` | `"sm" \| "md" \| "lg"` | no | `md` | - | TypeLiteral |
|
|
199
|
+
| `sortingFns` | `Record<string, SortingFn<any>>` | no | - | - | TypeLiteral |
|
|
200
|
+
| `SubRowComponent` | `((row: Row<TData>) => ReactNode)` | no | - | - | TypeLiteral |
|
|
201
|
+
|
|
202
|
+
## Stories
|
|
203
|
+
|
|
204
|
+
### Default
|
|
205
|
+
|
|
206
|
+
Use DataTable when a data set needs table semantics plus common interactions such as sorting, row selection, nested expansion, custom row actions, and pinned action or control columns.
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
const Default = () => <DataTable
|
|
210
|
+
enableSorting
|
|
211
|
+
enableRowSelection
|
|
212
|
+
enableExpanding
|
|
213
|
+
data={customRowsData}
|
|
214
|
+
columns={customRowsColumns}
|
|
215
|
+
onRowAction={logRowAction}
|
|
216
|
+
actions={rowActions('ellipsis')} />;
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Sorting
|
|
220
|
+
|
|
221
|
+
Use sorting when users need to compare rows by one or more column values. Sortable headers cycle through ascending, descending, and unsorted states.
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
const Sorting = () => <DataTable enableSorting />;
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Selecting
|
|
228
|
+
|
|
229
|
+
Use row selection when users need to choose individual rows or select all rows in the current table state for a follow-up action.
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
const Selecting = () => <DataTable enableRowSelection />;
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Nested Rows
|
|
236
|
+
|
|
237
|
+
Use nested rows when child records share the same shape as their parent row. Include `subRows` on the data and enable expansion controls.
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
const NestedRows = () => <DataTable enableExpanding />;
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Custom Rows
|
|
244
|
+
|
|
245
|
+
Use custom rows when an expanded row needs richer content than the standard column layout while remaining connected to the parent row.
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
const CustomRows = () => <DataTable
|
|
249
|
+
data={customRowsData}
|
|
250
|
+
columns={customRowsColumns}
|
|
251
|
+
actions={rowActions()}
|
|
252
|
+
SubRowComponent={ExpandedBio}
|
|
253
|
+
enableExpanding />;
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Actions
|
|
257
|
+
|
|
258
|
+
Use row actions for controls scoped to a single row. The actions cell is pinned to the right so those controls remain available while scrolling.
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
const Actions = () => <DataTable data={customerData} columns={customerColumns} actions={rowActions()} />;
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Row Action
|
|
265
|
+
|
|
266
|
+
Use row action behavior when the whole row should activate a primary row destination or action while preserving keyboard access.
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
const RowAction = () => <DataTable data={customerData} columns={customerColumns} onRowAction={logRowAction} />;
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Components
|
|
273
|
+
|
|
274
|
+
Use component cells when mapped values need Maxio components such as links, tags, or other inline display elements.
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
const Components = () => <DataTable pattern size="lg" />;
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Header Groups
|
|
281
|
+
|
|
282
|
+
Use header groups when related columns need a shared parent heading above their individual column headers.
|
|
283
|
+
|
|
284
|
+
```tsx
|
|
285
|
+
const HeaderGroups = () => <DataTable />;
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Column Pinning
|
|
289
|
+
|
|
290
|
+
Use column pinning when important identifying columns or action columns must stay visible while the table scrolls horizontally.
|
|
291
|
+
|
|
292
|
+
```tsx
|
|
293
|
+
const ColumnPinning = () => <div style={{ overflowX: 'auto' }}>
|
|
294
|
+
<DataTable
|
|
295
|
+
enableRowSelection
|
|
296
|
+
data={customerData}
|
|
297
|
+
columns={pinningDemoColumns}
|
|
298
|
+
actions={rowActions()}
|
|
299
|
+
state={{
|
|
300
|
+
columnPinning: {
|
|
301
|
+
left: ['Name', 'Number'],
|
|
302
|
+
right: ['Home Revenue Total (USD)'],
|
|
303
|
+
},
|
|
304
|
+
}} />
|
|
305
|
+
</div>;
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Custom
|
|
309
|
+
|
|
310
|
+
Use the primitive table components only for simple static tables that do not need sorting, selection, expansion, pinning, or TanStack Table state.
|
|
311
|
+
|
|
312
|
+
```tsx
|
|
313
|
+
const Custom = () => (
|
|
314
|
+
<Table>
|
|
315
|
+
<TableHead>
|
|
316
|
+
<TableRow isHeader>
|
|
317
|
+
<HeaderCell colSpan={2}>Financial Summary</HeaderCell>
|
|
318
|
+
</TableRow>
|
|
319
|
+
<TableRow isHeader>
|
|
320
|
+
<HeaderCell></HeaderCell>
|
|
321
|
+
<HeaderCell>USD</HeaderCell>
|
|
322
|
+
</TableRow>
|
|
323
|
+
</TableHead>
|
|
324
|
+
<TableBody>
|
|
325
|
+
<TableRow>
|
|
326
|
+
<TableCell>Total Transactions:</TableCell>
|
|
327
|
+
<TableCell>28,000.00</TableCell>
|
|
328
|
+
</TableRow>
|
|
329
|
+
<TableRow>
|
|
330
|
+
<TableCell>Total Revenue Scheduled:</TableCell>
|
|
331
|
+
<TableCell>28,000.00</TableCell>
|
|
332
|
+
</TableRow>
|
|
333
|
+
<TableRow>
|
|
334
|
+
<TableCell>Total Invoice Line Items Scheduled:</TableCell>
|
|
335
|
+
<TableCell>28,000.00</TableCell>
|
|
336
|
+
</TableRow>
|
|
337
|
+
<TableRow>
|
|
338
|
+
<TableCell>Total Open Balance:</TableCell>
|
|
339
|
+
<TableCell>7,437.00</TableCell>
|
|
340
|
+
</TableRow>
|
|
341
|
+
</TableBody>
|
|
342
|
+
</Table>
|
|
343
|
+
);
|
|
344
|
+
```
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Drawer
|
|
2
|
+
|
|
3
|
+
## Usage Guidelines
|
|
4
|
+
|
|
5
|
+
### Overview
|
|
6
|
+
|
|
7
|
+
#### When to Use
|
|
8
|
+
|
|
9
|
+
- Use Drawer for focused tasks that need more space than a Popover, such as forms, settings, or detail views.
|
|
10
|
+
- Use Drawer when the task should temporarily block interaction with the rest of the page.
|
|
11
|
+
- Use with DialogTrigger for uncontrolled usage, or pair `open`/`onOpenChange` with your own state for controlled usage.
|
|
12
|
+
|
|
13
|
+
#### When Not to Use
|
|
14
|
+
|
|
15
|
+
- Do not use Drawer for brief explanatory content or a small set of actions. Use Popover instead.
|
|
16
|
+
- Do not use Drawer for page-level navigation. Use SideNav instead.
|
|
17
|
+
- Do not use Drawer when the task is the primary reason the user is on the page. Use a dedicated page instead.
|
|
18
|
+
|
|
19
|
+
### Behavior
|
|
20
|
+
|
|
21
|
+
- **Mouse and touch**: clicking the backdrop or the close button dismisses the drawer when `backdrop` allows it.
|
|
22
|
+
- **Keyboard**: Escape dismisses the drawer. Tab and Shift+Tab cycle focus within the drawer while it is open.
|
|
23
|
+
- **Focus management**: focus moves into the drawer on open and returns to the trigger on close, handled by the underlying React Aria `Modal`/`Dialog`.
|
|
24
|
+
- **Placement**: use `placement` to slide the drawer in from the `left`, `right`, `top`, or `bottom` edge of the viewport.
|
|
25
|
+
- **Sizing**: use `size` to control the drawer's width (left/right) or height (top/bottom); `full` spans the entire viewport dimension.
|
|
26
|
+
- **Backdrop**: use `backdrop="dim"` for a scrim behind the drawer, or `backdrop="none"` when the surrounding context should remain fully visible.
|
|
27
|
+
|
|
28
|
+
### Accessibility
|
|
29
|
+
|
|
30
|
+
- The drawer renders with `role="dialog"` and is labeled by `title` automatically via `aria-labelledby`.
|
|
31
|
+
- Always provide a `title`; it is the drawer's accessible name and its visible heading.
|
|
32
|
+
- Use `description` for supporting context below the title rather than cramming it into `title`.
|
|
33
|
+
- The close button has a built-in "Close" accessible name; do not remove it as the only dismissal path when `backdrop="none"`.
|
|
34
|
+
- Keep focus indicators visible on interactive elements inside the drawer.
|
|
35
|
+
|
|
36
|
+
### Content
|
|
37
|
+
|
|
38
|
+
- Keep `title` short and specific to the task, such as "Configure Custom Pricing" rather than "Settings".
|
|
39
|
+
- Use `description` for a single line of secondary context, such as a category or breadcrumb-style label.
|
|
40
|
+
- Put primary and secondary actions in `footer` rather than at the end of the scrollable body.
|
|
41
|
+
- Keep body content scannable; long-form content should still fit the drawer's fixed width or height.
|
|
42
|
+
|
|
43
|
+
### Related
|
|
44
|
+
|
|
45
|
+
- **[Popover](components-popover.md)**: use for lighter-weight, anchored content that doesn't need to block the page.
|
|
46
|
+
- **[Button](components-buttons-button.md)**: use for the trigger that opens the drawer and for footer actions.
|
|
47
|
+
|
|
48
|
+
## React
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import { Drawer, DialogTrigger } from '@maxio-com/react-ui-components';
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Imports
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { Body, Button, DialogTrigger, Drawer } from "@maxio-com/react-ui-components";
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Prop Types
|
|
61
|
+
|
|
62
|
+
### Drawer
|
|
63
|
+
|
|
64
|
+
| Prop | Type | Required | Default | Description | Source |
|
|
65
|
+
| --- | --- | --- | --- | --- | --- |
|
|
66
|
+
| `backdrop` | `"none" \| "dim"` | no | `dim` | - | DrawerProps |
|
|
67
|
+
| `className` | `string` | no | - | - | DrawerProps |
|
|
68
|
+
| `description` | `string` | no | - | - | DrawerProps |
|
|
69
|
+
| `footer` | `ReactNode` | no | - | - | DrawerProps |
|
|
70
|
+
| `onOpenChange` | `((open: boolean) => void)` | no | - | - | DrawerProps |
|
|
71
|
+
| `open` | `boolean` | no | - | - | DrawerProps |
|
|
72
|
+
| `placement` | `"left" \| "right" \| "top" \| "bottom"` | no | `right` | - | DrawerProps |
|
|
73
|
+
| `size` | `"sm" \| "md" \| "lg" \| "xl" \| "full"` | no | `md` | - | DrawerProps |
|
|
74
|
+
| `title` | `string` | yes | - | - | DrawerProps |
|
|
75
|
+
|
|
76
|
+
## Stories
|
|
77
|
+
|
|
78
|
+
### Default
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
const Default = () => <DialogTrigger>
|
|
82
|
+
<Button variant="secondary" size="md">Open Drawer
|
|
83
|
+
</Button>
|
|
84
|
+
<Drawer placement="right" size="md" backdrop="dim" title="Drawer title">
|
|
85
|
+
<Body size="sm">{LOREM}</Body>
|
|
86
|
+
</Drawer>
|
|
87
|
+
</DialogTrigger>;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Controlled (external state)
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
const Controlled = () => {
|
|
94
|
+
const [open, setOpen] = React.useState(false);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<>
|
|
98
|
+
<Button variant="secondary" size="md" onClick={() => setOpen(true)}>Open Drawer
|
|
99
|
+
</Button>
|
|
100
|
+
<Drawer
|
|
101
|
+
placement="right"
|
|
102
|
+
size="md"
|
|
103
|
+
backdrop="dim"
|
|
104
|
+
title="Drawer title"
|
|
105
|
+
open={open}
|
|
106
|
+
onOpenChange={setOpen}>
|
|
107
|
+
<Body size="sm">{LOREM}</Body>
|
|
108
|
+
</Drawer>
|
|
109
|
+
</>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### With footer
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
const WithFooter = () => {
|
|
118
|
+
const [open, setOpen] = React.useState(false);
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<>
|
|
122
|
+
<Button variant="secondary" size="md" onClick={() => setOpen(true)}>Open Drawer
|
|
123
|
+
</Button>
|
|
124
|
+
<Drawer
|
|
125
|
+
placement="right"
|
|
126
|
+
size="md"
|
|
127
|
+
backdrop="dim"
|
|
128
|
+
open={open}
|
|
129
|
+
onOpenChange={setOpen}
|
|
130
|
+
title="Settings"
|
|
131
|
+
footer={
|
|
132
|
+
<>
|
|
133
|
+
<Button
|
|
134
|
+
variant="primary"
|
|
135
|
+
size="md"
|
|
136
|
+
onClick={() => setOpen(false)}
|
|
137
|
+
>
|
|
138
|
+
Save
|
|
139
|
+
</Button>
|
|
140
|
+
<Button
|
|
141
|
+
variant="secondary"
|
|
142
|
+
size="md"
|
|
143
|
+
onClick={() => setOpen(false)}
|
|
144
|
+
>
|
|
145
|
+
Cancel
|
|
146
|
+
</Button>
|
|
147
|
+
</>
|
|
148
|
+
}>
|
|
149
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
|
150
|
+
{PARAGRAPHS.map((p, i) => (
|
|
151
|
+
<Body key={i} size="sm">
|
|
152
|
+
{p}
|
|
153
|
+
</Body>
|
|
154
|
+
))}
|
|
155
|
+
</div>
|
|
156
|
+
</Drawer>
|
|
157
|
+
</>
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### All sides
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
const Sides = () => {
|
|
166
|
+
const placements: DrawerPlacement[] = ['right', 'left', 'top', 'bottom'];
|
|
167
|
+
const [openPlacement, setOpenPlacement] =
|
|
168
|
+
React.useState<DrawerPlacement | null>(null);
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem' }}>
|
|
172
|
+
{placements.map((placement) => (
|
|
173
|
+
<Button
|
|
174
|
+
key={placement}
|
|
175
|
+
variant="secondary"
|
|
176
|
+
size="md"
|
|
177
|
+
onClick={() => setOpenPlacement(placement)}
|
|
178
|
+
>
|
|
179
|
+
{placement}
|
|
180
|
+
</Button>
|
|
181
|
+
))}
|
|
182
|
+
{placements.map((placement) => (
|
|
183
|
+
<Drawer
|
|
184
|
+
key={placement}
|
|
185
|
+
open={openPlacement === placement}
|
|
186
|
+
onOpenChange={(o) => !o && setOpenPlacement(null)}
|
|
187
|
+
placement={placement}
|
|
188
|
+
size="md"
|
|
189
|
+
title={`${placement} drawer`}
|
|
190
|
+
>
|
|
191
|
+
<Body size="sm">Slides in from the {placement}.</Body>
|
|
192
|
+
</Drawer>
|
|
193
|
+
))}
|
|
194
|
+
</div>
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Backdrop variants
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
const BackdropVariants = () => {
|
|
203
|
+
const backdrops: DrawerBackdrop[] = ['dim', 'none'];
|
|
204
|
+
const [openBackdrop, setOpenBackdrop] =
|
|
205
|
+
React.useState<DrawerBackdrop | null>(null);
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem' }}>
|
|
209
|
+
{backdrops.map((b) => (
|
|
210
|
+
<Button
|
|
211
|
+
key={b}
|
|
212
|
+
variant="secondary"
|
|
213
|
+
size="md"
|
|
214
|
+
onClick={() => setOpenBackdrop(b)}
|
|
215
|
+
>
|
|
216
|
+
{b}
|
|
217
|
+
</Button>
|
|
218
|
+
))}
|
|
219
|
+
{backdrops.map((b) => (
|
|
220
|
+
<Drawer
|
|
221
|
+
key={b}
|
|
222
|
+
open={openBackdrop === b}
|
|
223
|
+
onOpenChange={(o) => !o && setOpenBackdrop(null)}
|
|
224
|
+
backdrop={b}
|
|
225
|
+
title={`backdrop="${b}"`}
|
|
226
|
+
>
|
|
227
|
+
<Body size="sm">Backdrop style: {b}</Body>
|
|
228
|
+
</Drawer>
|
|
229
|
+
))}
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
};
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Sizes
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
const Sizes = () => {
|
|
239
|
+
const sizes: DrawerSize[] = ['sm', 'md', 'lg', 'xl', 'full'];
|
|
240
|
+
const [openSize, setOpenSize] = React.useState<DrawerSize | null>(null);
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem' }}>
|
|
244
|
+
{sizes.map((s) => (
|
|
245
|
+
<Button
|
|
246
|
+
key={s}
|
|
247
|
+
variant="secondary"
|
|
248
|
+
size="md"
|
|
249
|
+
onClick={() => setOpenSize(s)}
|
|
250
|
+
>
|
|
251
|
+
{s}
|
|
252
|
+
</Button>
|
|
253
|
+
))}
|
|
254
|
+
{sizes.map((s) => (
|
|
255
|
+
<Drawer
|
|
256
|
+
key={s}
|
|
257
|
+
open={openSize === s}
|
|
258
|
+
onOpenChange={(o) => !o && setOpenSize(null)}
|
|
259
|
+
size={s}
|
|
260
|
+
title={`size="${s}"`}
|
|
261
|
+
>
|
|
262
|
+
<Body size="sm">Width: {s}</Body>
|
|
263
|
+
</Drawer>
|
|
264
|
+
))}
|
|
265
|
+
</div>
|
|
266
|
+
);
|
|
267
|
+
};
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### With description
|
|
271
|
+
|
|
272
|
+
```tsx
|
|
273
|
+
const WithDescription = () => {
|
|
274
|
+
const [open, setOpen] = React.useState(false);
|
|
275
|
+
|
|
276
|
+
return (
|
|
277
|
+
<>
|
|
278
|
+
<Button variant="secondary" size="md" onClick={() => setOpen(true)}>Open Drawer
|
|
279
|
+
</Button>
|
|
280
|
+
<Drawer
|
|
281
|
+
placement="right"
|
|
282
|
+
size="md"
|
|
283
|
+
backdrop="dim"
|
|
284
|
+
open={open}
|
|
285
|
+
onOpenChange={setOpen}
|
|
286
|
+
title="Configure Custom Pricing"
|
|
287
|
+
description="API Credits · Product">
|
|
288
|
+
<Body size="sm">{LOREM}</Body>
|
|
289
|
+
</Drawer>
|
|
290
|
+
</>
|
|
291
|
+
);
|
|
292
|
+
};
|
|
293
|
+
```
|