@ceed/ads 1.29.1 → 1.30.0-next.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.
Files changed (62) hide show
  1. package/dist/components/CurrencyInput/CurrencyInput.d.ts +1 -1
  2. package/dist/components/CurrencyInput/hooks/use-currency-setting.d.ts +2 -2
  3. package/dist/components/ProfileMenu/ProfileMenu.d.ts +1 -1
  4. package/dist/components/SearchBar/SearchBar.d.ts +21 -0
  5. package/dist/components/SearchBar/index.d.ts +3 -0
  6. package/dist/components/data-display/Badge.md +39 -71
  7. package/dist/components/data-display/DataTable.md +1 -1
  8. package/dist/components/data-display/InfoSign.md +98 -74
  9. package/dist/components/data-display/Typography.md +97 -363
  10. package/dist/components/feedback/Dialog.md +62 -76
  11. package/dist/components/feedback/Modal.md +44 -259
  12. package/dist/components/feedback/llms.txt +0 -2
  13. package/dist/components/index.d.ts +2 -0
  14. package/dist/components/inputs/Autocomplete.md +107 -356
  15. package/dist/components/inputs/ButtonGroup.md +106 -115
  16. package/dist/components/inputs/Calendar.md +459 -98
  17. package/dist/components/inputs/CurrencyInput.md +5 -183
  18. package/dist/components/inputs/DatePicker.md +431 -108
  19. package/dist/components/inputs/DateRangePicker.md +492 -131
  20. package/dist/components/inputs/FilterMenu.md +19 -169
  21. package/dist/components/inputs/FilterableCheckboxGroup.md +23 -123
  22. package/dist/components/inputs/IconButton.md +88 -137
  23. package/dist/components/inputs/Input.md +0 -5
  24. package/dist/components/inputs/MonthPicker.md +422 -95
  25. package/dist/components/inputs/MonthRangePicker.md +466 -89
  26. package/dist/components/inputs/PercentageInput.md +16 -185
  27. package/dist/components/inputs/RadioButton.md +35 -163
  28. package/dist/components/inputs/RadioTileGroup.md +61 -150
  29. package/dist/components/inputs/SearchBar.md +44 -0
  30. package/dist/components/inputs/Select.md +326 -222
  31. package/dist/components/inputs/Switch.md +376 -136
  32. package/dist/components/inputs/Textarea.md +10 -213
  33. package/dist/components/inputs/Uploader/Uploader.md +66 -145
  34. package/dist/components/inputs/llms.txt +1 -3
  35. package/dist/components/navigation/Breadcrumbs.md +322 -80
  36. package/dist/components/navigation/Dropdown.md +221 -92
  37. package/dist/components/navigation/IconMenuButton.md +502 -40
  38. package/dist/components/navigation/InsetDrawer.md +738 -68
  39. package/dist/components/navigation/Link.md +298 -39
  40. package/dist/components/navigation/Menu.md +285 -92
  41. package/dist/components/navigation/MenuButton.md +448 -55
  42. package/dist/components/navigation/Pagination.md +338 -47
  43. package/dist/components/navigation/ProfileMenu.md +268 -45
  44. package/dist/components/navigation/Stepper.md +28 -160
  45. package/dist/components/navigation/Tabs.md +316 -57
  46. package/dist/components/surfaces/Sheet.md +334 -151
  47. package/dist/index.browser.js +15 -13
  48. package/dist/index.browser.js.map +4 -4
  49. package/dist/index.cjs +289 -288
  50. package/dist/index.d.ts +1 -1
  51. package/dist/index.js +426 -369
  52. package/dist/llms.txt +1 -8
  53. package/framer/index.js +1 -1
  54. package/package.json +16 -15
  55. package/dist/chunks/rehype-accent-FZRUD7VI.js +0 -39
  56. package/dist/components/feedback/CircularProgress.md +0 -257
  57. package/dist/components/feedback/Skeleton.md +0 -280
  58. package/dist/components/inputs/FormControl.md +0 -361
  59. package/dist/components/inputs/RadioList.md +0 -241
  60. package/dist/components/inputs/Slider.md +0 -334
  61. package/dist/guides/ThemeProvider.md +0 -116
  62. package/dist/guides/llms.txt +0 -9
@@ -1,8 +1,8 @@
1
1
  # MenuButton
2
2
 
3
- MenuButton is a high-level compound component that combines a labeled button trigger with a dropdown menu in a single, prop-driven API. Pass `buttonText` and an `items` array and the component handles the Dropdown, Menu, and MenuItem composition internally. It is the recommended choice when you need a simple text-triggered menu without custom content inside the menu panel.
3
+ ## Introduction
4
4
 
5
- MenuButton belongs to the **menu component family** alongside Dropdown, Menu, MenuItem, and IconMenuButton. For icon-only triggers, use IconMenuButton. For menus that need custom content (headers, dividers, mixed elements), use the lower-level Dropdown + Menu composition instead.
5
+ MenuButton is a compound component that combines a button with a dropdown menu, allowing users to trigger a list of actions or navigation options from a single interactive element. It displays a button with text and an optional dropdown indicator icon that, when clicked, reveals a menu of selectable items. MenuButton is commonly used for action menus, navigation shortcuts, and grouped operations in toolbars and headers.
6
6
 
7
7
  ```tsx
8
8
  <MenuButton
@@ -34,6 +34,15 @@ MenuButton belongs to the **menu component family** alongside Dropdown, Menu, Me
34
34
  | color | — | "primary" |
35
35
  | size | — | — |
36
36
 
37
+ > ⚠️ **Usage Warning** ⚠️
38
+ >
39
+ > Choose the right component for your use case:
40
+ >
41
+ > - **MenuButton**: For action menus triggered by a labeled button
42
+ > - **IconMenuButton**: For action menus triggered by an icon-only button
43
+ > - **Dropdown**: For more complex menu structures with custom triggers
44
+ > - **Select**: For form inputs where user selects a value (not actions)
45
+
37
46
  ## Usage
38
47
 
39
48
  ```tsx
@@ -53,19 +62,31 @@ function UserMenu() {
53
62
  }
54
63
  ```
55
64
 
56
- ## When to Use Which
65
+ ## Examples
57
66
 
58
- | Component | Trigger | Content | Use when |
59
- | ------------------- | ----------- | ---------------- | ------------------------------------------------------------ |
60
- | **MenuButton** | Text button | Simple item list | You need a labeled trigger with a flat list of text options |
61
- | **IconMenuButton** | Icon button | Simple item list | Space is limited (tables, cards, toolbars) |
62
- | **Dropdown + Menu** | Any element | Rich content | You need dividers, headers, icons in items, or custom layout |
67
+ ### Playground
63
68
 
64
- ## Features
69
+ Interactive example with all controls.
65
70
 
66
- ### Standalone (Without Dropdown Icon)
71
+ ```tsx
72
+ <MenuButton
73
+ buttonText="Dashboard..."
74
+ items={[{
75
+ text: 'Profile'
76
+ }, {
77
+ text: 'My account'
78
+ }, {
79
+ text: 'Logout'
80
+ }]}
81
+ showIcon
82
+ variant="solid"
83
+ color="primary"
84
+ />
85
+ ```
67
86
 
68
- Use `showIcon={false}` and a custom `buttonComponent` to render MenuButton as a standalone navigation link without the dropdown indicator.
87
+ ### Standalone (Without Icon)
88
+
89
+ Use as a simple navigation link without the dropdown icon.
69
90
 
70
91
  ```tsx
71
92
  <MenuButton
@@ -89,7 +110,7 @@ Use `showIcon={false}` and a custom `buttonComponent` to render MenuButton as a
89
110
 
90
111
  ### With End Decorator
91
112
 
92
- Add badges, chips, or icons after the button text using `endDecorator`.
113
+ Add badges, chips, or icons at the end of the button.
93
114
 
94
115
  ```tsx
95
116
  <MenuButton
@@ -110,7 +131,7 @@ Add badges, chips, or icons after the button text using `endDecorator`.
110
131
 
111
132
  ### Placement: Bottom Start
112
133
 
113
- Menu aligns to the left edge of the trigger button.
134
+ Menu aligns to the left edge of the button.
114
135
 
115
136
  ```tsx
116
137
  <MenuButton
@@ -131,7 +152,7 @@ Menu aligns to the left edge of the trigger button.
131
152
 
132
153
  ### Placement: Bottom
133
154
 
134
- Menu centers below the trigger button.
155
+ Menu centers below the button.
135
156
 
136
157
  ```tsx
137
158
  <MenuButton
@@ -152,7 +173,7 @@ Menu centers below the trigger button.
152
173
 
153
174
  ### Placement: Bottom End
154
175
 
155
- Menu aligns to the right edge of the trigger button.
176
+ Menu aligns to the right edge of the button.
156
177
 
157
178
  ```tsx
158
179
  <MenuButton
@@ -171,6 +192,25 @@ Menu aligns to the right edge of the trigger button.
171
192
  />
172
193
  ```
173
194
 
195
+ ## When to Use
196
+
197
+ ### ✅ Good Use Cases
198
+
199
+ - **User account menus**: Profile, settings, logout actions
200
+ - **Action grouping**: Related actions in a toolbar
201
+ - **Navigation shortcuts**: Quick access to related pages
202
+ - **Overflow menus**: When space is limited
203
+ - **Context actions**: Page or section-specific actions
204
+ - **Export options**: Download, export, or share actions
205
+
206
+ ### ❌ When Not to Use
207
+
208
+ - **Form value selection**: Use Select for picking values in forms
209
+ - **Single action**: Use Button for single actions
210
+ - **Icon-only trigger**: Use IconMenuButton for minimal space
211
+ - **Complex menus**: Use Dropdown for nested menus or custom content
212
+ - **Primary navigation**: Use Navigator or Tabs for main navigation
213
+
174
214
  ## Common Use Cases
175
215
 
176
216
  ### User Account Menu
@@ -186,6 +226,7 @@ function AccountMenu({ user, onLogout }) {
186
226
  items={[
187
227
  { text: 'Profile', onClick: () => navigate('/profile') },
188
228
  { text: 'Account Settings', onClick: () => navigate('/settings') },
229
+ { text: 'Billing', onClick: () => navigate('/billing') },
189
230
  { text: 'Logout', onClick: onLogout },
190
231
  ]}
191
232
  />
@@ -193,10 +234,10 @@ function AccountMenu({ user, onLogout }) {
193
234
  }
194
235
  ```
195
236
 
196
- ### Export Options
237
+ ### Export Options Menu
197
238
 
198
239
  ```tsx
199
- function ExportMenu({ onExport }) {
240
+ function ExportMenu({ data, onExport }) {
200
241
  return (
201
242
  <MenuButton
202
243
  buttonText="Export"
@@ -206,73 +247,425 @@ function ExportMenu({ onExport }) {
206
247
  { text: 'Export as CSV', onClick: () => onExport('csv') },
207
248
  { text: 'Export as Excel', onClick: () => onExport('xlsx') },
208
249
  { text: 'Export as PDF', onClick: () => onExport('pdf') },
250
+ { text: 'Print', onClick: () => window.print() },
209
251
  ]}
210
252
  />
211
253
  );
212
254
  }
213
255
  ```
214
256
 
215
- ### Toolbar Actions
257
+ ### Actions Menu in Toolbar
216
258
 
217
259
  ```tsx
218
260
  function ToolbarActions({ selectedItems, onAction }) {
261
+ const hasSelection = selectedItems.length > 0;
262
+
263
+ return (
264
+ <Stack direction="row" gap={1}>
265
+ <Button onClick={() => onAction('create')}>Create New</Button>
266
+ <MenuButton
267
+ buttonText="Actions"
268
+ variant="outlined"
269
+ color="neutral"
270
+ disabled={!hasSelection}
271
+ items={[
272
+ { text: `Edit (${selectedItems.length})`, onClick: () => onAction('edit') },
273
+ { text: 'Duplicate', onClick: () => onAction('duplicate') },
274
+ { text: 'Move to Folder', onClick: () => onAction('move') },
275
+ { text: 'Archive', onClick: () => onAction('archive') },
276
+ { text: 'Delete', onClick: () => onAction('delete') },
277
+ ]}
278
+ />
279
+ </Stack>
280
+ );
281
+ }
282
+ ```
283
+
284
+ ### Navigation with Dropdown
285
+
286
+ ```tsx
287
+ function ProductsMenu() {
219
288
  return (
220
289
  <MenuButton
221
- buttonText="Actions"
222
- variant="outlined"
290
+ buttonText="Products"
291
+ variant="plain"
292
+ showIcon={true}
293
+ items={[
294
+ { text: 'All Products', onClick: () => navigate('/products') },
295
+ { text: 'Categories', onClick: () => navigate('/products/categories') },
296
+ { text: 'Inventory', onClick: () => navigate('/products/inventory') },
297
+ { text: 'Price Lists', onClick: () => navigate('/products/prices') },
298
+ ]}
299
+ />
300
+ );
301
+ }
302
+ ```
303
+
304
+ ### Status Change Menu
305
+
306
+ ```tsx
307
+ function StatusMenu({ currentStatus, onStatusChange }) {
308
+ const statuses = [
309
+ { value: 'draft', label: 'Draft', color: 'neutral' },
310
+ { value: 'pending', label: 'Pending Review', color: 'warning' },
311
+ { value: 'approved', label: 'Approved', color: 'success' },
312
+ { value: 'rejected', label: 'Rejected', color: 'danger' },
313
+ ];
314
+
315
+ const current = statuses.find((s) => s.value === currentStatus);
316
+
317
+ return (
318
+ <MenuButton
319
+ buttonText={current?.label || 'Set Status'}
320
+ variant="soft"
321
+ color={current?.color || 'neutral'}
322
+ items={statuses.map((status) => ({
323
+ text: status.label,
324
+ onClick: () => onStatusChange(status.value),
325
+ }))}
326
+ />
327
+ );
328
+ }
329
+ ```
330
+
331
+ ### Menu with Decorators
332
+
333
+ ```tsx
334
+ function NotificationsMenu({ notifications }) {
335
+ const unreadCount = notifications.filter((n) => !n.read).length;
336
+
337
+ return (
338
+ <MenuButton
339
+ buttonText="Notifications"
340
+ startDecorator={<NotificationsIcon />}
341
+ endDecorator={
342
+ unreadCount > 0 && (
343
+ <Chip size="sm" color="danger">
344
+ {unreadCount}
345
+ </Chip>
346
+ )
347
+ }
348
+ variant="soft"
349
+ items={notifications.slice(0, 5).map((n) => ({
350
+ text: n.title,
351
+ onClick: () => markAsRead(n.id),
352
+ }))}
353
+ />
354
+ );
355
+ }
356
+ ```
357
+
358
+ ### Quick Settings Menu
359
+
360
+ ```tsx
361
+ function QuickSettings({ settings, onSettingChange }) {
362
+ return (
363
+ <MenuButton
364
+ buttonText="Settings"
365
+ startDecorator={<SettingsIcon />}
366
+ variant="plain"
223
367
  color="neutral"
224
- disabled={selectedItems.length === 0}
225
368
  items={[
226
- { text: `Edit (${selectedItems.length})`, onClick: () => onAction('edit') },
227
- { text: 'Duplicate', onClick: () => onAction('duplicate') },
228
- { text: 'Archive', onClick: () => onAction('archive') },
229
- { text: 'Delete', onClick: () => onAction('delete') },
369
+ {
370
+ text: settings.darkMode ? '☀️ Light Mode' : '🌙 Dark Mode',
371
+ onClick: () => onSettingChange('darkMode', !settings.darkMode),
372
+ },
373
+ {
374
+ text: settings.compactView ? '📐 Normal View' : '📏 Compact View',
375
+ onClick: () => onSettingChange('compactView', !settings.compactView),
376
+ },
377
+ {
378
+ text: 'Language: ' + settings.language.toUpperCase(),
379
+ onClick: () => openLanguageModal(),
380
+ },
230
381
  ]}
231
382
  />
232
383
  );
233
384
  }
234
385
  ```
235
386
 
236
- ## Best Practices
387
+ ## Props and Customization
388
+
389
+ ### Key Props
390
+
391
+ | Prop | Type | Default | Description |
392
+ | ---------------------- | -------------------------------------------------------------- | ---------------- | --------------------------------- |
393
+ | `buttonText` | `string` | - | Text displayed on the button |
394
+ | `items` | `Array<{ text: string; onClick?: () => void }>` | - | Menu items to display |
395
+ | `showIcon` | `boolean` | `true` | Show dropdown indicator icon |
396
+ | `variant` | `'solid' \| 'soft' \| 'outlined' \| 'plain'` | `'solid'` | Button style variant |
397
+ | `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | `'primary'` | Button color |
398
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Button size |
399
+ | `placement` | `'bottom-start' \| 'bottom' \| 'bottom-end'` | `'bottom-start'` | Menu position |
400
+ | `startDecorator` | `ReactNode` | - | Content before button text |
401
+ | `endDecorator` | `ReactNode` | - | Content after button text |
402
+ | `buttonComponent` | `React.ElementType` | `Button` | Custom button component |
403
+ | `buttonComponentProps` | `object` | - | Props for custom button component |
404
+
405
+ ### Menu Item Structure
406
+
407
+ ```tsx
408
+ interface MenuItem {
409
+ text: string; // Display text
410
+ onClick?: () => void; // Click handler
411
+ // Additional props can be passed based on implementation
412
+ }
413
+
414
+ // Example items array
415
+ const items = [
416
+ { text: 'Edit', onClick: handleEdit },
417
+ { text: 'Delete', onClick: handleDelete },
418
+ ];
419
+ ```
420
+
421
+ ### Variant and Color Options
422
+
423
+ ```tsx
424
+ // Primary solid (default)
425
+ <MenuButton buttonText="Actions" variant="solid" color="primary" />
426
+
427
+ // Subtle outlined
428
+ <MenuButton buttonText="Actions" variant="outlined" color="neutral" />
429
+
430
+ // Soft background
431
+ <MenuButton buttonText="Actions" variant="soft" color="primary" />
432
+
433
+ // Plain/text only
434
+ <MenuButton buttonText="Actions" variant="plain" color="neutral" />
435
+
436
+ // Danger color
437
+ <MenuButton buttonText="Delete" variant="soft" color="danger" />
438
+ ```
439
+
440
+ ### Size Options
237
441
 
238
- - **Use descriptive button text.** The button label should clearly indicate what kind of options the menu contains. Avoid vague labels like "More" or "Options" -- prefer specific labels like "Export Options" or "User Actions".
442
+ ```tsx
443
+ // Small
444
+ <MenuButton buttonText="Small" size="sm" items={items} />
239
445
 
240
- ```tsx
241
- {/* Good: Clear purpose */}
242
- <MenuButton buttonText="Export Options" items={exportItems} />
446
+ // Medium (default)
447
+ <MenuButton buttonText="Medium" size="md" items={items} />
243
448
 
244
- {/* ❌ Avoid: Vague label */}
245
- <MenuButton buttonText="More" items={items} />
246
- ```
449
+ // Large
450
+ <MenuButton buttonText="Large" size="lg" items={items} />
451
+ ```
247
452
 
248
- - **Keep the items list short and focused.** Aim for 3-7 items that are logically related. If you need more items or mixed content, switch to the Dropdown + Menu composition pattern.
453
+ ### Custom Button Component
249
454
 
250
- - **Match variant to context.** Use `variant="solid"` for primary action menus, `variant="outlined"` or `variant="plain"` for secondary or navigation menus. Be consistent within the same toolbar or header.
455
+ ```tsx
456
+ // Use with custom components like Link
457
+ import { Link } from 'react-router-dom';
251
458
 
252
- ```tsx
253
- {/* ✅ Good: Appropriate variants */}
254
- <MenuButton buttonText="Create" variant="solid" color="primary" items={createItems} />
255
- <MenuButton buttonText="Settings" variant="plain" color="neutral" items={settingsItems} />
256
- ```
459
+ <MenuButton
460
+ buttonText="Dashboard"
461
+ buttonComponent={Link}
462
+ buttonComponentProps={{ to: '/dashboard' }}
463
+ showIcon={false}
464
+ items={items}
465
+ />
466
+ ```
257
467
 
258
- - **Do not use MenuButton for form value selection.** MenuButton triggers actions -- it is not a Select replacement. For form inputs where the user picks a value, use the Select component.
468
+ ### Decorators
259
469
 
260
- ```tsx
261
- {/* Bad: Form selection */}
262
- <MenuButton buttonText={selectedValue} items={options} />
470
+ ```tsx
471
+ // Start decorator (icon before text)
472
+ <MenuButton
473
+ buttonText="Export"
474
+ startDecorator={<DownloadIcon />}
475
+ items={exportItems}
476
+ />
263
477
 
264
- {/* Good: Use Select for forms */}
265
- <Select value={selectedValue} onChange={handleChange}>
266
- <Option value="a">Option A</Option>
267
- <Option value="b">Option B</Option>
268
- </Select>
269
- ```
478
+ // End decorator (badge after text)
479
+ <MenuButton
480
+ buttonText="Inbox"
481
+ endDecorator={<Chip size="sm">5</Chip>}
482
+ items={inboxItems}
483
+ />
270
484
 
271
- - **Use `placement` to prevent overflow.** When the trigger is near the right edge of the viewport, use `placement="bottom-end"` so the menu does not extend beyond the screen.
485
+ // Both decorators
486
+ <MenuButton
487
+ buttonText="User"
488
+ startDecorator={<Avatar size="sm" />}
489
+ endDecorator={<ChevronDownIcon />}
490
+ showIcon={false}
491
+ items={userItems}
492
+ />
493
+ ```
272
494
 
273
495
  ## Accessibility
274
496
 
275
- - **ARIA attributes**: The trigger button automatically receives `aria-haspopup="true"` and `aria-expanded` that reflects the open/closed state. The menu panel has `role="menu"` and each item has `role="menuitem"`.
276
- - **Keyboard navigation**: Enter or Space opens the menu. Arrow Down/Up moves between items. Escape closes the menu and returns focus to the button. Tab closes the menu and moves focus to the next element.
277
- - **Screen reader support**: The button is announced as "\[buttonText], menu button, expanded/collapsed". Menu items are announced with their position (e.g., "Profile, menu item, 1 of 3").
278
- - **Focus management**: Focus moves to the first menu item when the menu opens and returns to the trigger button when the menu closes.
497
+ MenuButton includes built-in accessibility features:
498
+
499
+ ### ARIA Attributes
500
+
501
+ - Button has `aria-haspopup="true"` indicating it opens a menu
502
+ - Button has `aria-expanded` reflecting menu open state
503
+ - Menu has proper `role="menu"` and `role="menuitem"` structure
504
+ - Focus is managed between button and menu items
505
+
506
+ ### Keyboard Navigation
507
+
508
+ - **Enter/Space**: Open menu when button is focused
509
+ - **Arrow Down**: Move to next menu item
510
+ - **Arrow Up**: Move to previous menu item
511
+ - **Escape**: Close menu and return focus to button
512
+ - **Tab**: Close menu and move to next focusable element
513
+ - **Enter**: Select focused menu item
514
+
515
+ ### Screen Reader Support
516
+
517
+ ```tsx
518
+ // Button announces: "Account, menu button, expanded/collapsed"
519
+ <MenuButton
520
+ buttonText="Account"
521
+ items={[...]}
522
+ />
523
+
524
+ // Menu items are announced as: "Profile, menu item, 1 of 3"
525
+ ```
526
+
527
+ ### Focus Management
528
+
529
+ - Focus moves to first menu item when menu opens
530
+ - Focus returns to button when menu closes
531
+ - Menu items are focusable with arrow keys
532
+
533
+ ## Best Practices
534
+
535
+ ### ✅ Do
536
+
537
+ 1. **Use clear, action-oriented labels**: Button text should indicate what the menu contains
538
+
539
+ ```tsx
540
+ // ✅ Good: Clear menu purpose
541
+ <MenuButton buttonText="Export Options" items={exportItems} />
542
+ <MenuButton buttonText="User Actions" items={userItems} />
543
+ ```
544
+
545
+ 2. **Group related actions**: Keep menu items logically related
546
+
547
+ ```tsx
548
+ // ✅ Good: Related file actions
549
+ <MenuButton
550
+ buttonText="File"
551
+ items={[
552
+ { text: 'New', onClick: handleNew },
553
+ { text: 'Open', onClick: handleOpen },
554
+ { text: 'Save', onClick: handleSave },
555
+ { text: 'Save As...', onClick: handleSaveAs },
556
+ ]}
557
+ />
558
+ ```
559
+
560
+ 3. **Use appropriate variants**: Match the button style to its context
561
+
562
+ ```tsx
563
+ // ✅ Good: Primary for main actions, plain for navigation
564
+ <MenuButton buttonText="Create" variant="solid" color="primary" />
565
+ <MenuButton buttonText="Settings" variant="plain" color="neutral" />
566
+ ```
567
+
568
+ 4. **Limit menu items**: Keep menus scannable (5-7 items max)
569
+
570
+ ### ❌ Don't
571
+
572
+ 1. **Don't use for form inputs**: Use Select for value selection
573
+
574
+ ```tsx
575
+ // ❌ Bad: Using MenuButton for form selection
576
+ <MenuButton buttonText={selectedOption} items={options} />
577
+
578
+ // ✅ Good: Use Select for forms
579
+ <Select value={selectedOption} onChange={handleChange}>
580
+ {options.map((opt) => <Option key={opt.value} value={opt.value}>{opt.label}</Option>)}
581
+ </Select>
582
+ ```
583
+
584
+ 2. **Don't use vague labels**: Be specific about menu contents
585
+
586
+ ```tsx
587
+ // ❌ Bad: Unclear what "More" contains
588
+ <MenuButton buttonText="More" items={items} />
589
+
590
+ // ✅ Good: Clear menu purpose
591
+ <MenuButton buttonText="More Actions" items={actionItems} />
592
+ ```
593
+
594
+ 3. **Don't overload menus**: Split large menus into categories
595
+
596
+ ```tsx
597
+ // ❌ Bad: Too many unrelated items
598
+ <MenuButton
599
+ buttonText="Options"
600
+ items={[
601
+ { text: 'Edit' },
602
+ { text: 'Delete' },
603
+ { text: 'Share' },
604
+ { text: 'Settings' },
605
+ { text: 'Help' },
606
+ { text: 'About' },
607
+ // ... many more items
608
+ ]}
609
+ />
610
+ ```
611
+
612
+ 4. **Don't hide primary actions**: Important actions should be visible
613
+
614
+ ## Performance Considerations
615
+
616
+ ### Memoize Items Array
617
+
618
+ Prevent unnecessary re-renders by memoizing the items array:
619
+
620
+ ```tsx
621
+ const menuItems = useMemo(
622
+ () => [
623
+ { text: 'Edit', onClick: handleEdit },
624
+ { text: 'Delete', onClick: handleDelete },
625
+ ],
626
+ [handleEdit, handleDelete]
627
+ );
628
+
629
+ <MenuButton buttonText="Actions" items={menuItems} />
630
+ ```
631
+
632
+ ### Memoize Click Handlers
633
+
634
+ ```tsx
635
+ const handleExport = useCallback(
636
+ (format: string) => {
637
+ exportData(data, format);
638
+ },
639
+ [data]
640
+ );
641
+
642
+ const exportItems = useMemo(
643
+ () => [
644
+ { text: 'CSV', onClick: () => handleExport('csv') },
645
+ { text: 'Excel', onClick: () => handleExport('xlsx') },
646
+ ],
647
+ [handleExport]
648
+ );
649
+ ```
650
+
651
+ ### Lazy Load Heavy Actions
652
+
653
+ For actions that trigger heavy operations:
654
+
655
+ ```tsx
656
+ const items = useMemo(
657
+ () => [
658
+ {
659
+ text: 'Generate Report',
660
+ onClick: async () => {
661
+ setLoading(true);
662
+ await generateReport();
663
+ setLoading(false);
664
+ },
665
+ },
666
+ ],
667
+ []
668
+ );
669
+ ```
670
+
671
+ MenuButton provides a convenient way to group related actions behind a single button trigger. Use it for account menus, action groups, and navigation shortcuts while keeping the menu contents focused and scannable. For icon-only triggers in space-constrained areas, consider IconMenuButton instead.