torch-glare 2.5.6 → 2.6.0
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/README.md +4 -3
- package/dist/bin/index.js +9 -24
- package/dist/bin/index.js.map +1 -1
- package/dist/src/commands/add.d.ts +2 -2
- package/dist/src/commands/add.d.ts.map +1 -1
- package/dist/src/commands/add.js +4 -70
- package/dist/src/commands/add.js.map +1 -1
- package/dist/src/commands/hook.d.ts +5 -4
- package/dist/src/commands/hook.d.ts.map +1 -1
- package/dist/src/commands/hook.js +7 -75
- package/dist/src/commands/hook.js.map +1 -1
- package/dist/src/commands/init.js +1 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/layout.d.ts +5 -4
- package/dist/src/commands/layout.d.ts.map +1 -1
- package/dist/src/commands/layout.js +7 -75
- package/dist/src/commands/layout.js.map +1 -1
- package/dist/src/commands/provider.d.ts +5 -4
- package/dist/src/commands/provider.d.ts.map +1 -1
- package/dist/src/commands/provider.js +7 -75
- package/dist/src/commands/provider.js.map +1 -1
- package/dist/src/commands/update.d.ts.map +1 -1
- package/dist/src/commands/update.js +78 -43
- package/dist/src/commands/update.js.map +1 -1
- package/dist/src/commands/utils.d.ts +5 -4
- package/dist/src/commands/utils.d.ts.map +1 -1
- package/dist/src/commands/utils.js +7 -80
- package/dist/src/commands/utils.js.map +1 -1
- package/dist/src/shared/addFromRegistry.d.ts +16 -0
- package/dist/src/shared/addFromRegistry.d.ts.map +1 -0
- package/dist/src/shared/addFromRegistry.js +88 -0
- package/dist/src/shared/addFromRegistry.js.map +1 -0
- package/dist/src/shared/getInstallCommand.d.ts.map +1 -1
- package/dist/src/shared/getInstallCommand.js +5 -0
- package/dist/src/shared/getInstallCommand.js.map +1 -1
- package/dist/src/shared/installDependencies.d.ts +0 -10
- package/dist/src/shared/installDependencies.d.ts.map +1 -1
- package/dist/src/shared/installDependencies.js +0 -17
- package/dist/src/shared/installDependencies.js.map +1 -1
- package/dist/src/shared/installFromPlan.d.ts +7 -5
- package/dist/src/shared/installFromPlan.d.ts.map +1 -1
- package/dist/src/shared/installFromPlan.js +103 -36
- package/dist/src/shared/installFromPlan.js.map +1 -1
- package/dist/src/shared/loadRegistry.d.ts +12 -3
- package/dist/src/shared/loadRegistry.d.ts.map +1 -1
- package/dist/src/shared/loadRegistry.js +19 -20
- package/dist/src/shared/loadRegistry.js.map +1 -1
- package/dist/src/shared/registryClient.d.ts +19 -0
- package/dist/src/shared/registryClient.d.ts.map +1 -0
- package/dist/src/shared/registryClient.js +128 -0
- package/dist/src/shared/registryClient.js.map +1 -0
- package/dist/src/shared/resolveEntry.d.ts +10 -7
- package/dist/src/shared/resolveEntry.d.ts.map +1 -1
- package/dist/src/shared/resolveEntry.js +18 -13
- package/dist/src/shared/resolveEntry.js.map +1 -1
- package/dist/src/shared/suggestOtherCommand.d.ts +7 -1
- package/dist/src/shared/suggestOtherCommand.d.ts.map +1 -1
- package/dist/src/shared/suggestOtherCommand.js +29 -23
- package/dist/src/shared/suggestOtherCommand.js.map +1 -1
- package/dist/src/types/main.d.ts +31 -2
- package/dist/src/types/main.d.ts.map +1 -1
- package/package.json +6 -11
- package/apps/lib/components/ActionButton.tsx +0 -93
- package/apps/lib/components/ActionsGroup.tsx +0 -27
- package/apps/lib/components/AlertDialog.tsx +0 -204
- package/apps/lib/components/Avatar.tsx +0 -46
- package/apps/lib/components/Badge.tsx +0 -250
- package/apps/lib/components/BadgeField.tsx +0 -403
- package/apps/lib/components/Breadcrumb.tsx +0 -280
- package/apps/lib/components/Button.tsx +0 -325
- package/apps/lib/components/ButtonGroup.tsx +0 -190
- package/apps/lib/components/Calendar.tsx +0 -115
- package/apps/lib/components/Card.tsx +0 -95
- package/apps/lib/components/Checkbox.tsx +0 -42
- package/apps/lib/components/ColorPicker.tsx +0 -441
- package/apps/lib/components/ConclusionHeader.tsx +0 -148
- package/apps/lib/components/ContextMenu.tsx +0 -571
- package/apps/lib/components/CountBadge.tsx +0 -52
- package/apps/lib/components/DataTable.tsx +0 -213
- package/apps/lib/components/DataViews/badge.ts +0 -49
- package/apps/lib/components/DataViews/cell.tsx +0 -324
- package/apps/lib/components/DataViews/context.ts +0 -144
- package/apps/lib/components/DataViews/data-views.tsx +0 -395
- package/apps/lib/components/DataViews/filters/children.tsx +0 -98
- package/apps/lib/components/DataViews/filters/custom.tsx +0 -34
- package/apps/lib/components/DataViews/filters/filters.tsx +0 -157
- package/apps/lib/components/DataViews/filters/index.ts +0 -4
- package/apps/lib/components/DataViews/filters/labelled.tsx +0 -20
- package/apps/lib/components/DataViews/filters/presets.tsx +0 -65
- package/apps/lib/components/DataViews/filters/sync.tsx +0 -35
- package/apps/lib/components/DataViews/filters/values.ts +0 -173
- package/apps/lib/components/DataViews/header.tsx +0 -220
- package/apps/lib/components/DataViews/hooks/index.ts +0 -5
- package/apps/lib/components/DataViews/hooks/useActiveRow.ts +0 -22
- package/apps/lib/components/DataViews/hooks/useControllable.ts +0 -52
- package/apps/lib/components/DataViews/index.ts +0 -88
- package/apps/lib/components/DataViews/panel/columns.tsx +0 -153
- package/apps/lib/components/DataViews/panel/controls.tsx +0 -106
- package/apps/lib/components/DataViews/panel/index.ts +0 -3
- package/apps/lib/components/DataViews/panel/panel.tsx +0 -164
- package/apps/lib/components/DataViews/panel/saved-views.tsx +0 -67
- package/apps/lib/components/DataViews/panel/section.tsx +0 -81
- package/apps/lib/components/DataViews/panel/sort.tsx +0 -42
- package/apps/lib/components/DataViews/panel/tab.tsx +0 -31
- package/apps/lib/components/DataViews/slots.ts +0 -72
- package/apps/lib/components/DataViews/states.tsx +0 -73
- package/apps/lib/components/DataViews/types.ts +0 -510
- package/apps/lib/components/DataViews/views/board-view.tsx +0 -381
- package/apps/lib/components/DataViews/views/card-rows.tsx +0 -36
- package/apps/lib/components/DataViews/views/inbox-view.tsx +0 -260
- package/apps/lib/components/DataViews/views/pane-views.tsx +0 -196
- package/apps/lib/components/DataViews/views/table-view.tsx +0 -493
- package/apps/lib/components/DataViews/views/tree-view.tsx +0 -367
- package/apps/lib/components/DatePicker.tsx +0 -232
- package/apps/lib/components/Dialog.tsx +0 -116
- package/apps/lib/components/Divider.tsx +0 -28
- package/apps/lib/components/Drawer.tsx +0 -451
- package/apps/lib/components/DropdownMenu.tsx +0 -554
- package/apps/lib/components/FieldHint.tsx +0 -69
- package/apps/lib/components/Form.tsx +0 -182
- package/apps/lib/components/FormBuilder/context.ts +0 -85
- package/apps/lib/components/FormBuilder/field-kind.ts +0 -28
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +0 -60
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +0 -59
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +0 -11
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +0 -32
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +0 -74
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +0 -168
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +0 -39
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +0 -132
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +0 -109
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +0 -114
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +0 -157
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +0 -67
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +0 -41
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +0 -336
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +0 -213
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +0 -43
- package/apps/lib/components/FormBuilder/fields/countries.ts +0 -303
- package/apps/lib/components/FormBuilder/fields/index.ts +0 -25
- package/apps/lib/components/FormBuilder/form-builder.tsx +0 -198
- package/apps/lib/components/FormBuilder/index.ts +0 -31
- package/apps/lib/components/FormBuilder/numberFormat.ts +0 -16
- package/apps/lib/components/FormBuilder/submit.tsx +0 -67
- package/apps/lib/components/FormBuilder/types.ts +0 -316
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +0 -250
- package/apps/lib/components/FormRenderer/detail.tsx +0 -256
- package/apps/lib/components/FormRenderer/form-renderer.tsx +0 -358
- package/apps/lib/components/FormRenderer/header.tsx +0 -88
- package/apps/lib/components/FormRenderer/index.ts +0 -17
- package/apps/lib/components/FormRenderer/notch-action.tsx +0 -64
- package/apps/lib/components/FormRenderer/section.tsx +0 -39
- package/apps/lib/components/FormRenderer/stepper.tsx +0 -393
- package/apps/lib/components/FormRenderer/types.ts +0 -115
- package/apps/lib/components/FormSummary.tsx +0 -282
- package/apps/lib/components/HeaderBar.tsx +0 -117
- package/apps/lib/components/ImageAttachment.tsx +0 -209
- package/apps/lib/components/InnerLabelField.tsx +0 -154
- package/apps/lib/components/Input.tsx +0 -224
- package/apps/lib/components/InputField.tsx +0 -147
- package/apps/lib/components/InputOTP.tsx +0 -84
- package/apps/lib/components/Label.tsx +0 -129
- package/apps/lib/components/LabelField.tsx +0 -76
- package/apps/lib/components/LabeledCheckBox.tsx +0 -51
- package/apps/lib/components/LabeledRadio.tsx +0 -51
- package/apps/lib/components/LinkButton.tsx +0 -94
- package/apps/lib/components/LoginButton.tsx +0 -51
- package/apps/lib/components/PasswordLevel.tsx +0 -58
- package/apps/lib/components/Popover.tsx +0 -312
- package/apps/lib/components/ProfileMenu.tsx +0 -85
- package/apps/lib/components/Radio.tsx +0 -59
- package/apps/lib/components/RadioCard.tsx +0 -50
- package/apps/lib/components/ScrollArea.tsx +0 -54
- package/apps/lib/components/SearchField.tsx +0 -62
- package/apps/lib/components/SearchableSelect.tsx +0 -318
- package/apps/lib/components/SearchableTable.tsx +0 -344
- package/apps/lib/components/SearchableTree.tsx +0 -485
- package/apps/lib/components/SearchableTreeDialog.tsx +0 -502
- package/apps/lib/components/SectionBlock.tsx +0 -168
- package/apps/lib/components/Select.tsx +0 -347
- package/apps/lib/components/SimpleSelect.tsx +0 -225
- package/apps/lib/components/Skeleton.tsx +0 -12
- package/apps/lib/components/SlideDatePicker.tsx +0 -255
- package/apps/lib/components/SpinLoading.tsx +0 -174
- package/apps/lib/components/Stepper.tsx +0 -480
- package/apps/lib/components/Switch.tsx +0 -82
- package/apps/lib/components/TabFormItem.tsx +0 -153
- package/apps/lib/components/TabSwitch.tsx +0 -172
- package/apps/lib/components/Table.tsx +0 -597
- package/apps/lib/components/TextEditor/ChartBlockTool.ts +0 -676
- package/apps/lib/components/TextEditor/RichTextField.tsx +0 -46
- package/apps/lib/components/TextEditor/TableDnDWrapper.ts +0 -462
- package/apps/lib/components/TextEditor/TextEditor.tsx +0 -1164
- package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +0 -429
- package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +0 -70
- package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +0 -50
- package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +0 -48
- package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +0 -98
- package/apps/lib/components/TextEditor/editorjs.d.ts +0 -60
- package/apps/lib/components/TextEditor/index.ts +0 -7
- package/apps/lib/components/TextEditor/markdownParser.ts +0 -346
- package/apps/lib/components/Textarea.tsx +0 -117
- package/apps/lib/components/Timeline.tsx +0 -263
- package/apps/lib/components/Toast.tsx +0 -87
- package/apps/lib/components/Toggle.tsx +0 -125
- package/apps/lib/components/ToggleButton.tsx +0 -140
- package/apps/lib/components/Tooltip.tsx +0 -116
- package/apps/lib/components/TransparentLabel.tsx +0 -71
- package/apps/lib/components/TreeDropDown.tsx +0 -115
- package/apps/lib/components/TreeFolder/TreeFolder.tsx +0 -380
- package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +0 -75
- package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +0 -354
- package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +0 -60
- package/apps/lib/components/TreeFolder/icons.tsx +0 -60
- package/apps/lib/components/TreeFolder/index.ts +0 -24
- package/apps/lib/components/TreeFolder/treeFolderUtils.ts +0 -109
- package/apps/lib/components/TreeFolder/types.ts +0 -77
- package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +0 -118
- package/apps/lib/hooks/useActiveTreeItem.ts +0 -61
- package/apps/lib/hooks/useClickOutside.ts +0 -27
- package/apps/lib/hooks/useDragDrop.tsx +0 -365
- package/apps/lib/hooks/useHtmlDir.ts +0 -31
- package/apps/lib/hooks/useInfiniteScroll.ts +0 -108
- package/apps/lib/hooks/useIsMobile.ts +0 -21
- package/apps/lib/hooks/useResize.ts +0 -67
- package/apps/lib/hooks/useTagSelection.ts +0 -300
- package/apps/lib/layouts/CNLayout.tsx +0 -372
- package/apps/lib/layouts/DataViewCard.tsx +0 -62
- package/apps/lib/layouts/FieldSection.tsx +0 -91
- package/apps/lib/layouts/TreeSubLayout.tsx +0 -173
- package/apps/lib/providers/ThemeProvider.tsx +0 -84
- package/apps/lib/registry.json +0 -1217
- package/apps/lib/tsconfig.json +0 -9
- package/apps/lib/tsconfig.tsbuildinfo +0 -1
- package/apps/lib/utils/cn.ts +0 -6
- package/apps/lib/utils/color.ts +0 -175
- package/apps/lib/utils/dataViews/path.ts +0 -67
- package/apps/lib/utils/dataViews/query.ts +0 -73
- package/apps/lib/utils/dataViews/types.ts +0 -187
- package/apps/lib/utils/dateFormat.ts +0 -108
- package/apps/lib/utils/resize.ts +0 -31
- package/apps/lib/utils/scroller.ts +0 -26
- package/apps/lib/utils/types.ts +0 -16
- package/docs/BLOCKS.md +0 -129
- package/docs/CHANGELOG-1.1.16.md +0 -108
- package/docs/Cover.png +0 -0
- package/docs/README.md +0 -215
- package/docs/components/action-button.md +0 -638
- package/docs/components/actions-group.md +0 -708
- package/docs/components/alert-dialog.md +0 -920
- package/docs/components/avatar.md +0 -709
- package/docs/components/badge-field.md +0 -738
- package/docs/components/badge.md +0 -301
- package/docs/components/breadcrumb.md +0 -930
- package/docs/components/button-group.md +0 -650
- package/docs/components/button.md +0 -639
- package/docs/components/calendar.md +0 -555
- package/docs/components/card.md +0 -622
- package/docs/components/chart-block-tool.md +0 -129
- package/docs/components/checkbox.md +0 -699
- package/docs/components/cn-layout.md +0 -854
- package/docs/components/color-picker.md +0 -101
- package/docs/components/conclusion-header.md +0 -80
- package/docs/components/context-menu.md +0 -483
- package/docs/components/count-badge.md +0 -560
- package/docs/components/data-table.md +0 -854
- package/docs/components/data-views/backend-response.md +0 -324
- package/docs/components/data-views/examples/a11y-rtl.md +0 -250
- package/docs/components/data-views/examples/api-orders-route.md +0 -130
- package/docs/components/data-views/examples/fields.md +0 -362
- package/docs/components/data-views/examples/filters.md +0 -307
- package/docs/components/data-views/examples/inbox-routing.md +0 -218
- package/docs/components/data-views/examples/index.md +0 -29
- package/docs/components/data-views/examples/overview.md +0 -244
- package/docs/components/data-views/examples/panel.md +0 -212
- package/docs/components/data-views/examples/scale.md +0 -231
- package/docs/components/data-views/examples/server-side.md +0 -210
- package/docs/components/data-views/examples/state.md +0 -250
- package/docs/components/data-views/examples/tree-custom.md +0 -388
- package/docs/components/data-views/examples/view-registry.md +0 -313
- package/docs/components/data-views/examples/views.md +0 -534
- package/docs/components/data-views/guide.md +0 -405
- package/docs/components/data-views/index.md +0 -1521
- package/docs/components/data-views/migration.md +0 -81
- package/docs/components/date-picker.md +0 -857
- package/docs/components/dialog.md +0 -929
- package/docs/components/divider.md +0 -766
- package/docs/components/drawer.md +0 -654
- package/docs/components/dropdown-menu.md +0 -668
- package/docs/components/field-hint.md +0 -898
- package/docs/components/field-section.md +0 -781
- package/docs/components/form-builder.md +0 -284
- package/docs/components/form-renderer.md +0 -396
- package/docs/components/form-summary.md +0 -138
- package/docs/components/form.md +0 -785
- package/docs/components/header-bar.md +0 -188
- package/docs/components/image-attachment.md +0 -906
- package/docs/components/inner-label-field.md +0 -819
- package/docs/components/input-field.md +0 -623
- package/docs/components/input-otp.md +0 -756
- package/docs/components/input.md +0 -634
- package/docs/components/label-field.md +0 -770
- package/docs/components/label.md +0 -761
- package/docs/components/labeled-check-box.md +0 -642
- package/docs/components/labeled-radio.md +0 -796
- package/docs/components/link-button.md +0 -685
- package/docs/components/login-button.md +0 -872
- package/docs/components/password-level.md +0 -803
- package/docs/components/popover.md +0 -649
- package/docs/components/profile-menu.md +0 -656
- package/docs/components/radio-card.md +0 -801
- package/docs/components/radio.md +0 -735
- package/docs/components/scroll-area.md +0 -691
- package/docs/components/search-field.md +0 -702
- package/docs/components/searchable-select.md +0 -368
- package/docs/components/searchable-table.md +0 -438
- package/docs/components/searchable-tree-dialog.md +0 -190
- package/docs/components/searchable-tree.md +0 -200
- package/docs/components/section-block.md +0 -483
- package/docs/components/select.md +0 -709
- package/docs/components/simple-select.md +0 -741
- package/docs/components/skeleton.md +0 -706
- package/docs/components/slide-date-picker.md +0 -837
- package/docs/components/spin-loading.md +0 -696
- package/docs/components/stepper.md +0 -316
- package/docs/components/switch.md +0 -770
- package/docs/components/tab-form-item.md +0 -773
- package/docs/components/tab-switch.md +0 -170
- package/docs/components/table-dnd-wrapper.md +0 -107
- package/docs/components/table.md +0 -1088
- package/docs/components/text-editor.md +0 -735
- package/docs/components/textarea.md +0 -681
- package/docs/components/timeline.md +0 -254
- package/docs/components/toast.md +0 -773
- package/docs/components/toggle-button.md +0 -643
- package/docs/components/toggle.md +0 -882
- package/docs/components/tooltip.md +0 -513
- package/docs/components/transparent-label.md +0 -721
- package/docs/components/tree-drop-down.md +0 -740
- package/docs/components/tree-folder.md +0 -110
- package/docs/components/tree-sub-layout.md +0 -707
- package/docs/explanation/architecture.md +0 -54
- package/docs/explanation/design-system.md +0 -52
- package/docs/how-to/form-and-list-recipes.md +0 -386
- package/docs/how-to/forms-with-form-builder.md +0 -472
- package/docs/how-to/guides.md +0 -1027
- package/docs/migration/changelog.md +0 -60
- package/docs/migration/form-builder-2.5.2.md +0 -113
- package/docs/reference/cli.md +0 -112
- package/docs/reference/components.md +0 -572
- package/docs/reference/hooks.md +0 -1523
- package/docs/reference/providers.md +0 -866
- package/docs/reference/tailwind-plugins.md +0 -942
- package/docs/reference/theme.md +0 -85
- package/docs/reference/types.md +0 -1016
- package/docs/reference/utilities.md +0 -726
- package/docs/tutorials/building-first-form.md +0 -846
- package/docs/tutorials/component-composition.md +0 -882
- package/docs/tutorials/getting-started.md +0 -302
- package/docs/tutorials/theming-basics.md +0 -785
|
@@ -1,668 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: DropdownMenu
|
|
3
|
-
description: Comprehensive dropdown menu component with rich features including sub-menus, checkboxes, radio groups, auto-grouping, and keyboard navigation
|
|
4
|
-
group: Overlays & Dialogs
|
|
5
|
-
keywords: [dropdown-menu, menu, radix-ui, submenu, checkbox, radio, auto-group]
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# DropdownMenu
|
|
9
|
-
|
|
10
|
-
> A feature-rich dropdown menu component with support for nested submenus, checkbox items, radio groups, auto-grouped boxed sections, and keyboard shortcuts. Perfect for application menus and complex action lists. For right-click menus, see [ContextMenu](./context-menu.md).
|
|
11
|
-
|
|
12
|
-
## Installation
|
|
13
|
-
|
|
14
|
-
TORCH Glare is a copy-in library: the CLI copies this component's source into your project
|
|
15
|
-
(you do **not** install it from the npm package). Run `init` once, then `add`:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npx torch-glare@latest init
|
|
19
|
-
npx torch-glare@latest add DropdownMenu
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
`add` also copies any components, hooks, and utilities that `DropdownMenu` depends on.
|
|
23
|
-
|
|
24
|
-
## Import
|
|
25
|
-
|
|
26
|
-
Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
|
|
27
|
-
|
|
28
|
-
```tsx
|
|
29
|
-
import { DropdownMenu } from "@/components/DropdownMenu";
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Quick Examples
|
|
33
|
-
|
|
34
|
-
### Basic Menu
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@/components/DropdownMenu";
|
|
38
|
-
import { Button } from "@/components/Button";
|
|
39
|
-
|
|
40
|
-
function Example() {
|
|
41
|
-
return (
|
|
42
|
-
<DropdownMenu>
|
|
43
|
-
<DropdownMenuTrigger asChild>
|
|
44
|
-
<Button>Open Menu</Button>
|
|
45
|
-
</DropdownMenuTrigger>
|
|
46
|
-
<DropdownMenuContent>
|
|
47
|
-
<DropdownMenuItem>Profile</DropdownMenuItem>
|
|
48
|
-
<DropdownMenuItem>Settings</DropdownMenuItem>
|
|
49
|
-
<DropdownMenuItem>Logout</DropdownMenuItem>
|
|
50
|
-
</DropdownMenuContent>
|
|
51
|
-
</DropdownMenu>
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### With Icons
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@/components/DropdownMenu";
|
|
60
|
-
|
|
61
|
-
function IconMenu() {
|
|
62
|
-
return (
|
|
63
|
-
<DropdownMenu>
|
|
64
|
-
<DropdownMenuTrigger asChild>
|
|
65
|
-
<button>
|
|
66
|
-
<i className="ri-more-2-fill" />
|
|
67
|
-
</button>
|
|
68
|
-
</DropdownMenuTrigger>
|
|
69
|
-
<DropdownMenuContent>
|
|
70
|
-
<DropdownMenuItem>
|
|
71
|
-
<i className="ri-edit-line" />
|
|
72
|
-
<span>Edit</span>
|
|
73
|
-
</DropdownMenuItem>
|
|
74
|
-
<DropdownMenuItem>
|
|
75
|
-
<i className="ri-share-line" />
|
|
76
|
-
<span>Share</span>
|
|
77
|
-
</DropdownMenuItem>
|
|
78
|
-
<DropdownMenuItem variant="Negative">
|
|
79
|
-
<i className="ri-delete-bin-line" />
|
|
80
|
-
<span>Delete</span>
|
|
81
|
-
</DropdownMenuItem>
|
|
82
|
-
</DropdownMenuContent>
|
|
83
|
-
</DropdownMenu>
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### With Keyboard Shortcuts
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuShortcut } from "@/components/DropdownMenu";
|
|
92
|
-
|
|
93
|
-
function ShortcutMenu() {
|
|
94
|
-
return (
|
|
95
|
-
<DropdownMenu>
|
|
96
|
-
<DropdownMenuTrigger asChild>
|
|
97
|
-
<button>File</button>
|
|
98
|
-
</DropdownMenuTrigger>
|
|
99
|
-
<DropdownMenuContent>
|
|
100
|
-
<DropdownMenuItem>
|
|
101
|
-
New File
|
|
102
|
-
<DropdownMenuShortcut>⌘N</DropdownMenuShortcut>
|
|
103
|
-
</DropdownMenuItem>
|
|
104
|
-
<DropdownMenuItem>
|
|
105
|
-
Save
|
|
106
|
-
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
|
107
|
-
</DropdownMenuItem>
|
|
108
|
-
<DropdownMenuItem>
|
|
109
|
-
Save As...
|
|
110
|
-
<DropdownMenuShortcut>⌘⇧S</DropdownMenuShortcut>
|
|
111
|
-
</DropdownMenuItem>
|
|
112
|
-
</DropdownMenuContent>
|
|
113
|
-
</DropdownMenu>
|
|
114
|
-
)
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### With Labels and Grouping
|
|
119
|
-
|
|
120
|
-
Labels act as section boundaries. The loose items between labels are automatically wrapped in boxed groups — no separator component needed.
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel } from "@/components/DropdownMenu";
|
|
124
|
-
|
|
125
|
-
function OrganizedMenu() {
|
|
126
|
-
return (
|
|
127
|
-
<DropdownMenu>
|
|
128
|
-
<DropdownMenuTrigger asChild>
|
|
129
|
-
<button>Actions</button>
|
|
130
|
-
</DropdownMenuTrigger>
|
|
131
|
-
<DropdownMenuContent>
|
|
132
|
-
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
133
|
-
<DropdownMenuItem>Profile</DropdownMenuItem>
|
|
134
|
-
<DropdownMenuItem>Billing</DropdownMenuItem>
|
|
135
|
-
|
|
136
|
-
<DropdownMenuLabel>Settings</DropdownMenuLabel>
|
|
137
|
-
<DropdownMenuItem>Preferences</DropdownMenuItem>
|
|
138
|
-
<DropdownMenuItem>Keyboard Shortcuts</DropdownMenuItem>
|
|
139
|
-
|
|
140
|
-
<DropdownMenuItem variant="Negative">Logout</DropdownMenuItem>
|
|
141
|
-
</DropdownMenuContent>
|
|
142
|
-
</DropdownMenu>
|
|
143
|
-
)
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### With Checkboxes
|
|
148
|
-
|
|
149
|
-
Checkbox and radio items keep the menu **open** when toggled (the built-in `onSelect` calls `preventDefault`), so users can change several options without the menu closing each time.
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem } from "@/components/DropdownMenu";
|
|
153
|
-
import { useState } from 'react'
|
|
154
|
-
|
|
155
|
-
function CheckboxMenu() {
|
|
156
|
-
const [showStatusBar, setShowStatusBar] = useState(true)
|
|
157
|
-
const [showActivityBar, setShowActivityBar] = useState(false)
|
|
158
|
-
const [showPanel, setShowPanel] = useState(false)
|
|
159
|
-
|
|
160
|
-
return (
|
|
161
|
-
<DropdownMenu>
|
|
162
|
-
<DropdownMenuTrigger asChild>
|
|
163
|
-
<button>View</button>
|
|
164
|
-
</DropdownMenuTrigger>
|
|
165
|
-
<DropdownMenuContent>
|
|
166
|
-
<DropdownMenuCheckboxItem
|
|
167
|
-
checked={showStatusBar}
|
|
168
|
-
onCheckedChange={setShowStatusBar}
|
|
169
|
-
>
|
|
170
|
-
Status Bar
|
|
171
|
-
</DropdownMenuCheckboxItem>
|
|
172
|
-
<DropdownMenuCheckboxItem
|
|
173
|
-
checked={showActivityBar}
|
|
174
|
-
onCheckedChange={setShowActivityBar}
|
|
175
|
-
>
|
|
176
|
-
Activity Bar
|
|
177
|
-
</DropdownMenuCheckboxItem>
|
|
178
|
-
<DropdownMenuCheckboxItem
|
|
179
|
-
checked={showPanel}
|
|
180
|
-
onCheckedChange={setShowPanel}
|
|
181
|
-
>
|
|
182
|
-
Panel
|
|
183
|
-
</DropdownMenuCheckboxItem>
|
|
184
|
-
</DropdownMenuContent>
|
|
185
|
-
</DropdownMenu>
|
|
186
|
-
)
|
|
187
|
-
}
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### With Radio Group
|
|
191
|
-
|
|
192
|
-
```typescript
|
|
193
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem } from "@/components/DropdownMenu";
|
|
194
|
-
import { useState } from 'react'
|
|
195
|
-
|
|
196
|
-
function RadioMenu() {
|
|
197
|
-
const [position, setPosition] = useState('bottom')
|
|
198
|
-
|
|
199
|
-
return (
|
|
200
|
-
<DropdownMenu>
|
|
201
|
-
<DropdownMenuTrigger asChild>
|
|
202
|
-
<button>Position</button>
|
|
203
|
-
</DropdownMenuTrigger>
|
|
204
|
-
<DropdownMenuContent>
|
|
205
|
-
<DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
|
|
206
|
-
<DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
|
|
207
|
-
<DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
|
|
208
|
-
<DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
|
|
209
|
-
</DropdownMenuRadioGroup>
|
|
210
|
-
</DropdownMenuContent>
|
|
211
|
-
</DropdownMenu>
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### With Submenu
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent } from "@/components/DropdownMenu";
|
|
220
|
-
|
|
221
|
-
function SubmenuExample() {
|
|
222
|
-
return (
|
|
223
|
-
<DropdownMenu>
|
|
224
|
-
<DropdownMenuTrigger asChild>
|
|
225
|
-
<button>More</button>
|
|
226
|
-
</DropdownMenuTrigger>
|
|
227
|
-
<DropdownMenuContent>
|
|
228
|
-
<DropdownMenuItem>New Tab</DropdownMenuItem>
|
|
229
|
-
<DropdownMenuItem>New Window</DropdownMenuItem>
|
|
230
|
-
|
|
231
|
-
<DropdownMenuSub>
|
|
232
|
-
<DropdownMenuSubTrigger>
|
|
233
|
-
More Tools
|
|
234
|
-
</DropdownMenuSubTrigger>
|
|
235
|
-
<DropdownMenuSubContent>
|
|
236
|
-
<DropdownMenuItem>Developer Tools</DropdownMenuItem>
|
|
237
|
-
<DropdownMenuItem>Task Manager</DropdownMenuItem>
|
|
238
|
-
<DropdownMenuItem>Extensions</DropdownMenuItem>
|
|
239
|
-
</DropdownMenuSubContent>
|
|
240
|
-
</DropdownMenuSub>
|
|
241
|
-
|
|
242
|
-
<DropdownMenuItem>Print</DropdownMenuItem>
|
|
243
|
-
</DropdownMenuContent>
|
|
244
|
-
</DropdownMenu>
|
|
245
|
-
)
|
|
246
|
-
}
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### Disabled Items
|
|
250
|
-
|
|
251
|
-
```typescript
|
|
252
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@/components/DropdownMenu";
|
|
253
|
-
|
|
254
|
-
function DisabledMenu() {
|
|
255
|
-
return (
|
|
256
|
-
<DropdownMenu>
|
|
257
|
-
<DropdownMenuTrigger asChild>
|
|
258
|
-
<button>Edit</button>
|
|
259
|
-
</DropdownMenuTrigger>
|
|
260
|
-
<DropdownMenuContent>
|
|
261
|
-
<DropdownMenuItem>Cut</DropdownMenuItem>
|
|
262
|
-
<DropdownMenuItem>Copy</DropdownMenuItem>
|
|
263
|
-
<DropdownMenuItem disabled>Paste (Empty Clipboard)</DropdownMenuItem>
|
|
264
|
-
</DropdownMenuContent>
|
|
265
|
-
</DropdownMenu>
|
|
266
|
-
)
|
|
267
|
-
}
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
### Right-click Menu
|
|
271
|
-
|
|
272
|
-
For a true right-click (context) menu, use the dedicated [ContextMenu](./context-menu.md) component instead of DropdownMenu — it opens at the pointer on right-click / long-press.
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
import { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem } from "@/components/ContextMenu";
|
|
276
|
-
|
|
277
|
-
function Example() {
|
|
278
|
-
return (
|
|
279
|
-
<ContextMenu>
|
|
280
|
-
<ContextMenuTrigger className="rounded-md border border-dashed p-8">
|
|
281
|
-
Right-click here
|
|
282
|
-
</ContextMenuTrigger>
|
|
283
|
-
<ContextMenuContent>
|
|
284
|
-
<ContextMenuItem>View</ContextMenuItem>
|
|
285
|
-
<ContextMenuItem>Edit</ContextMenuItem>
|
|
286
|
-
<ContextMenuItem variant="Negative">Delete</ContextMenuItem>
|
|
287
|
-
</ContextMenuContent>
|
|
288
|
-
</ContextMenu>
|
|
289
|
-
)
|
|
290
|
-
}
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### Long Menu (max height + scroll)
|
|
294
|
-
|
|
295
|
-
Tall menus scroll instead of overflowing off-screen. The surface caps at `maxHeight` (default `320`px) and never exceeds the space available after collision handling. Pass `maxHeight` to change the cap.
|
|
296
|
-
|
|
297
|
-
The panel itself does not scroll — it clips, and an inner viewport inside it does the scrolling. That keeps the panel's 4px frosted gutter fixed instead of scrolling away with the rows. Submenus behave identically and take their own `maxHeight`.
|
|
298
|
-
|
|
299
|
-
```typescript
|
|
300
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel } from "@/components/DropdownMenu";
|
|
301
|
-
import { Button } from "@/components/Button";
|
|
302
|
-
|
|
303
|
-
function LongMenu() {
|
|
304
|
-
return (
|
|
305
|
-
<DropdownMenu>
|
|
306
|
-
<DropdownMenuTrigger asChild>
|
|
307
|
-
<Button variant="BorderStyle">Jump to section</Button>
|
|
308
|
-
</DropdownMenuTrigger>
|
|
309
|
-
{/* Cap the surface at 240px — the rest scrolls. */}
|
|
310
|
-
<DropdownMenuContent align="start" maxHeight={240}>
|
|
311
|
-
<DropdownMenuLabel>Sections</DropdownMenuLabel>
|
|
312
|
-
{Array.from({ length: 20 }, (_, i) => (
|
|
313
|
-
<DropdownMenuItem key={i}>Section {i + 1}</DropdownMenuItem>
|
|
314
|
-
))}
|
|
315
|
-
</DropdownMenuContent>
|
|
316
|
-
</DropdownMenu>
|
|
317
|
-
)
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
## API Reference
|
|
322
|
-
|
|
323
|
-
### DropdownMenu (Root)
|
|
324
|
-
|
|
325
|
-
| Prop | Type | Default | Description |
|
|
326
|
-
|------|------|---------|-------------|
|
|
327
|
-
| `open` | `boolean` | - | Controlled open state |
|
|
328
|
-
| `onOpenChange` | `(open: boolean) => void` | - | Callback when state changes |
|
|
329
|
-
| `defaultOpen` | `boolean` | `false` | Uncontrolled default open state |
|
|
330
|
-
| `modal` | `boolean` | `true` | Whether to block outside interactions |
|
|
331
|
-
|
|
332
|
-
### DropdownMenuTrigger
|
|
333
|
-
|
|
334
|
-
| Prop | Type | Default | Description |
|
|
335
|
-
|------|------|---------|-------------|
|
|
336
|
-
| `asChild` | `boolean` | `false` | Render trigger as child element |
|
|
337
|
-
|
|
338
|
-
### DropdownMenuContent
|
|
339
|
-
|
|
340
|
-
| Prop | Type | Default | Description |
|
|
341
|
-
|------|------|---------|-------------|
|
|
342
|
-
| `variant` | `'PresentationStyle'` | `'PresentationStyle'` | Visual style variant |
|
|
343
|
-
| `theme` | `'dark' \| 'light' \| 'default'` | - | Theme variant |
|
|
344
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
345
|
-
| `sideOffset` | `number` | `4` | Distance from trigger |
|
|
346
|
-
| `collisionPadding` | `number` | `8` | Gap kept from viewport edges when flipping/shifting |
|
|
347
|
-
| `align` | `'start' \| 'center' \| 'end'` | `'center'` | Alignment (inherited from Radix) |
|
|
348
|
-
| `maxHeight` | `number` | `320` | Max height (px) of the surface before it scrolls. Capped at `min(maxHeight, available-height)` so the menu never overflows off-screen |
|
|
349
|
-
| `autoGroup` | `boolean` | `true` | Auto-wrap loose items in boxed groups |
|
|
350
|
-
|
|
351
|
-
### DropdownMenuItem
|
|
352
|
-
|
|
353
|
-
| Prop | Type | Default | Description |
|
|
354
|
-
|------|------|---------|-------------|
|
|
355
|
-
| `variant` | `'Default' \| 'info' \| 'Negative'` | `'Default'` | Item style variant |
|
|
356
|
-
| `size` | `'S' \| 'M'` | `'M'` | Item size |
|
|
357
|
-
| `inset` | `boolean` | `false` | Add left padding to align with items that have icons |
|
|
358
|
-
| `disabled` | `boolean` | `false` | Disabled state |
|
|
359
|
-
| `active` | `boolean` | `false` | Active state |
|
|
360
|
-
| `onSelect` | `(event) => void` | - | Select handler |
|
|
361
|
-
|
|
362
|
-
### DropdownMenuCheckboxItem
|
|
363
|
-
|
|
364
|
-
| Prop | Type | Default | Description |
|
|
365
|
-
|------|------|---------|-------------|
|
|
366
|
-
| `checked` | `boolean \| 'indeterminate'` | `false` | Checked state |
|
|
367
|
-
| `onCheckedChange` | `(checked: boolean) => void` | - | Change handler |
|
|
368
|
-
| `variant` | `'Default' \| 'info' \| 'Negative'` | `'Default'` | Style variant |
|
|
369
|
-
| `size` | `'S' \| 'M'` | `'M'` | Item size |
|
|
370
|
-
|
|
371
|
-
### DropdownMenuRadioGroup
|
|
372
|
-
|
|
373
|
-
| Prop | Type | Default | Description |
|
|
374
|
-
|------|------|---------|-------------|
|
|
375
|
-
| `value` | `string` | - | Selected radio value |
|
|
376
|
-
| `onValueChange` | `(value: string) => void` | - | Change handler |
|
|
377
|
-
|
|
378
|
-
### DropdownMenuRadioItem
|
|
379
|
-
|
|
380
|
-
| Prop | Type | Default | Description |
|
|
381
|
-
|------|------|---------|-------------|
|
|
382
|
-
| `value` | `string` | Required | Radio option value |
|
|
383
|
-
| `variant` | `'Default' \| 'info' \| 'Negative'` | `'Default'` | Style variant |
|
|
384
|
-
| `size` | `'S' \| 'M'` | `'M'` | Item size |
|
|
385
|
-
|
|
386
|
-
### DropdownMenuSubTrigger
|
|
387
|
-
|
|
388
|
-
| Prop | Type | Default | Description |
|
|
389
|
-
|------|------|---------|-------------|
|
|
390
|
-
| `variant` | `'Default' \| 'info' \| 'Negative'` | `'Default'` | Style variant |
|
|
391
|
-
| `size` | `'S' \| 'M'` | `'M'` | Item size |
|
|
392
|
-
| `inset` | `boolean` | `false` | Add left padding to align with items that have icons |
|
|
393
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
394
|
-
|
|
395
|
-
### DropdownMenuSubContent
|
|
396
|
-
|
|
397
|
-
| Prop | Type | Default | Description |
|
|
398
|
-
|------|------|---------|-------------|
|
|
399
|
-
| `variant` | `'PresentationStyle'` | `'PresentationStyle'` | Visual style variant |
|
|
400
|
-
| `autoGroup` | `boolean` | `true` | Auto-wrap loose items in boxed groups |
|
|
401
|
-
| `maxHeight` | `number` | `320` | Max height (px) before the submenu scrolls. Same `min(maxHeight, available-height)` cap as `DropdownMenuContent` |
|
|
402
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
403
|
-
|
|
404
|
-
### DropdownMenuLabel
|
|
405
|
-
|
|
406
|
-
| Prop | Type | Default | Description |
|
|
407
|
-
|------|------|---------|-------------|
|
|
408
|
-
| `inset` | `boolean` | `false` | Add left padding to align with items that have icons |
|
|
409
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
410
|
-
|
|
411
|
-
### DropdownMenuGroup
|
|
412
|
-
|
|
413
|
-
| Prop | Type | Default | Description |
|
|
414
|
-
|------|------|---------|-------------|
|
|
415
|
-
| `variant` | `'Boxed' \| 'Plain'` | `'Boxed'` | Boxed renders a bordered container; Plain is semantic-only |
|
|
416
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
417
|
-
|
|
418
|
-
### DropdownMenuShortcut
|
|
419
|
-
|
|
420
|
-
| Prop | Type | Default | Description |
|
|
421
|
-
|------|------|---------|-------------|
|
|
422
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
423
|
-
|
|
424
|
-
## TypeScript
|
|
425
|
-
|
|
426
|
-
### Full Type Definitions
|
|
427
|
-
|
|
428
|
-
```typescript
|
|
429
|
-
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
|
|
430
|
-
|
|
431
|
-
// Root
|
|
432
|
-
interface DropdownMenuProps {
|
|
433
|
-
open?: boolean
|
|
434
|
-
onOpenChange?: (open: boolean) => void
|
|
435
|
-
defaultOpen?: boolean
|
|
436
|
-
modal?: boolean
|
|
437
|
-
children: React.ReactNode
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
export const DropdownMenu: React.FC<DropdownMenuProps>
|
|
441
|
-
|
|
442
|
-
// Content
|
|
443
|
-
interface DropdownMenuContentProps {
|
|
444
|
-
variant?: 'PresentationStyle'
|
|
445
|
-
theme?: 'dark' | 'light' | 'default'
|
|
446
|
-
className?: string
|
|
447
|
-
sideOffset?: number
|
|
448
|
-
collisionPadding?: number
|
|
449
|
-
align?: 'start' | 'center' | 'end'
|
|
450
|
-
maxHeight?: number // default 320 — surface scrolls past this
|
|
451
|
-
autoGroup?: boolean
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
export const DropdownMenuContent: React.ForwardRefExoticComponent<DropdownMenuContentProps>
|
|
455
|
-
|
|
456
|
-
// Item
|
|
457
|
-
interface DropdownMenuItemProps {
|
|
458
|
-
variant?: 'Default' | 'info' | 'Negative'
|
|
459
|
-
size?: 'S' | 'M'
|
|
460
|
-
inset?: boolean
|
|
461
|
-
disabled?: boolean
|
|
462
|
-
active?: boolean
|
|
463
|
-
onSelect?: (event: Event) => void
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
export const DropdownMenuItem: React.ForwardRefExoticComponent<DropdownMenuItemProps>
|
|
467
|
-
|
|
468
|
-
// CheckboxItem
|
|
469
|
-
interface DropdownMenuCheckboxItemProps {
|
|
470
|
-
checked?: boolean | 'indeterminate'
|
|
471
|
-
onCheckedChange?: (checked: boolean) => void
|
|
472
|
-
variant?: 'Default' | 'info' | 'Negative'
|
|
473
|
-
size?: 'S' | 'M'
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
export const DropdownMenuCheckboxItem: React.ForwardRefExoticComponent<DropdownMenuCheckboxItemProps>
|
|
477
|
-
|
|
478
|
-
// RadioItem
|
|
479
|
-
interface DropdownMenuRadioItemProps {
|
|
480
|
-
value: string
|
|
481
|
-
variant?: 'Default' | 'info' | 'Negative'
|
|
482
|
-
size?: 'S' | 'M'
|
|
483
|
-
onSelect?: (event: Event) => void
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
export const DropdownMenuRadioItem: React.ForwardRefExoticComponent<DropdownMenuRadioItemProps>
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
## Common Patterns
|
|
490
|
-
|
|
491
|
-
### With State Management
|
|
492
|
-
|
|
493
|
-
```typescript
|
|
494
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@/components/DropdownMenu";
|
|
495
|
-
import { useState } from 'react'
|
|
496
|
-
|
|
497
|
-
function useDropdownMenu() {
|
|
498
|
-
const [open, setOpen] = useState(false)
|
|
499
|
-
|
|
500
|
-
const handleSelect = (action: string) => {
|
|
501
|
-
console.log(action)
|
|
502
|
-
setOpen(false)
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
return { open, setOpen, handleSelect }
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
function StatefulMenu() {
|
|
509
|
-
const { open, setOpen, handleSelect } = useDropdownMenu()
|
|
510
|
-
|
|
511
|
-
return (
|
|
512
|
-
<DropdownMenu open={open} onOpenChange={setOpen}>
|
|
513
|
-
<DropdownMenuTrigger>Actions</DropdownMenuTrigger>
|
|
514
|
-
<DropdownMenuContent>
|
|
515
|
-
<DropdownMenuItem onSelect={() => handleSelect('edit')}>
|
|
516
|
-
Edit
|
|
517
|
-
</DropdownMenuItem>
|
|
518
|
-
<DropdownMenuItem onSelect={() => handleSelect('delete')}>
|
|
519
|
-
Delete
|
|
520
|
-
</DropdownMenuItem>
|
|
521
|
-
</DropdownMenuContent>
|
|
522
|
-
</DropdownMenu>
|
|
523
|
-
)
|
|
524
|
-
}
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
## Testing
|
|
528
|
-
|
|
529
|
-
### Unit Test Example
|
|
530
|
-
|
|
531
|
-
```typescript
|
|
532
|
-
import { render, screen, fireEvent } from '@testing-library/react'
|
|
533
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@/components/DropdownMenu";
|
|
534
|
-
|
|
535
|
-
describe('DropdownMenu', () => {
|
|
536
|
-
it('opens on trigger click', () => {
|
|
537
|
-
render(
|
|
538
|
-
<DropdownMenu>
|
|
539
|
-
<DropdownMenuTrigger>Open</DropdownMenuTrigger>
|
|
540
|
-
<DropdownMenuContent>
|
|
541
|
-
<DropdownMenuItem>Item 1</DropdownMenuItem>
|
|
542
|
-
</DropdownMenuContent>
|
|
543
|
-
</DropdownMenu>
|
|
544
|
-
)
|
|
545
|
-
|
|
546
|
-
fireEvent.click(screen.getByText('Open'))
|
|
547
|
-
expect(screen.getByText('Item 1')).toBeInTheDocument()
|
|
548
|
-
})
|
|
549
|
-
|
|
550
|
-
it('calls onSelect handler', () => {
|
|
551
|
-
const handleSelect = jest.fn()
|
|
552
|
-
|
|
553
|
-
render(
|
|
554
|
-
<DropdownMenu defaultOpen>
|
|
555
|
-
<DropdownMenuContent>
|
|
556
|
-
<DropdownMenuItem onSelect={handleSelect}>
|
|
557
|
-
Select me
|
|
558
|
-
</DropdownMenuItem>
|
|
559
|
-
</DropdownMenuContent>
|
|
560
|
-
</DropdownMenu>
|
|
561
|
-
)
|
|
562
|
-
|
|
563
|
-
fireEvent.click(screen.getByText('Select me'))
|
|
564
|
-
expect(handleSelect).toHaveBeenCalled()
|
|
565
|
-
})
|
|
566
|
-
|
|
567
|
-
it('handles checkbox state', () => {
|
|
568
|
-
const handleChange = jest.fn()
|
|
569
|
-
|
|
570
|
-
render(
|
|
571
|
-
<DropdownMenu defaultOpen>
|
|
572
|
-
<DropdownMenuContent>
|
|
573
|
-
<DropdownMenuCheckboxItem
|
|
574
|
-
checked={false}
|
|
575
|
-
onCheckedChange={handleChange}
|
|
576
|
-
>
|
|
577
|
-
Checkbox
|
|
578
|
-
</DropdownMenuCheckboxItem>
|
|
579
|
-
</DropdownMenuContent>
|
|
580
|
-
</DropdownMenu>
|
|
581
|
-
)
|
|
582
|
-
|
|
583
|
-
fireEvent.click(screen.getByText('Checkbox'))
|
|
584
|
-
expect(handleChange).toHaveBeenCalledWith(true)
|
|
585
|
-
})
|
|
586
|
-
})
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
## Accessibility
|
|
590
|
-
|
|
591
|
-
- **Keyboard Support**:
|
|
592
|
-
- Arrow Down: Open menu / next item
|
|
593
|
-
- Arrow Up: Previous item
|
|
594
|
-
- Arrow Right: Open submenu
|
|
595
|
-
- Arrow Left: Close submenu
|
|
596
|
-
- Enter/Space: Select item
|
|
597
|
-
- Escape: Close menu
|
|
598
|
-
- Tab: Navigate away (closes menu)
|
|
599
|
-
- **ARIA Attributes**: Automatically applied by Radix UI
|
|
600
|
-
- **Focus Management**: Keyboard navigation fully supported
|
|
601
|
-
- **Screen Readers**: Menu structure announced
|
|
602
|
-
|
|
603
|
-
### Best Practices
|
|
604
|
-
|
|
605
|
-
```typescript
|
|
606
|
-
// Provide clear labels
|
|
607
|
-
<DropdownMenuTrigger aria-label="Open user menu">
|
|
608
|
-
<i className="ri-user-line" />
|
|
609
|
-
</DropdownMenuTrigger>
|
|
610
|
-
|
|
611
|
-
// Group related items
|
|
612
|
-
<DropdownMenuGroup>
|
|
613
|
-
<DropdownMenuItem>Profile</DropdownMenuItem>
|
|
614
|
-
<DropdownMenuItem>Settings</DropdownMenuItem>
|
|
615
|
-
</DropdownMenuGroup>
|
|
616
|
-
|
|
617
|
-
// Mark disabled with descriptive text
|
|
618
|
-
<DropdownMenuItem disabled>
|
|
619
|
-
Save (No changes to save)
|
|
620
|
-
</DropdownMenuItem>
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
## Performance
|
|
624
|
-
|
|
625
|
-
| Metric | Value |
|
|
626
|
-
|--------|-------|
|
|
627
|
-
| Bundle size (minified) | ~8kb |
|
|
628
|
-
| Bundle size (gzipped) | ~3kb |
|
|
629
|
-
| Dependencies | @radix-ui/react-dropdown-menu (~15kb) |
|
|
630
|
-
| Max height | Radix available height (scrollable) |
|
|
631
|
-
| Tree-shakeable | ✅ |
|
|
632
|
-
|
|
633
|
-
## Best Practices
|
|
634
|
-
|
|
635
|
-
1. **Use labels for organization**
|
|
636
|
-
```typescript
|
|
637
|
-
<DropdownMenuLabel>Section</DropdownMenuLabel>
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
2. **Show keyboard shortcuts**
|
|
641
|
-
```typescript
|
|
642
|
-
<DropdownMenuItem>
|
|
643
|
-
Save
|
|
644
|
-
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
|
645
|
-
</DropdownMenuItem>
|
|
646
|
-
```
|
|
647
|
-
|
|
648
|
-
3. **Group related items**
|
|
649
|
-
```typescript
|
|
650
|
-
<DropdownMenuGroup>
|
|
651
|
-
{/* Related items */}
|
|
652
|
-
</DropdownMenuGroup>
|
|
653
|
-
```
|
|
654
|
-
|
|
655
|
-
4. **Use appropriate variants**
|
|
656
|
-
```typescript
|
|
657
|
-
<DropdownMenuItem variant="Negative">Delete</DropdownMenuItem>
|
|
658
|
-
```
|
|
659
|
-
|
|
660
|
-
5. **Handle selection**: Close menu or update state
|
|
661
|
-
6. **Avoid deeply nested submenus**: 2 levels max
|
|
662
|
-
7. **Keep menu items concise**: Short, action-oriented labels
|
|
663
|
-
|
|
664
|
-
## Related Components
|
|
665
|
-
|
|
666
|
-
- [Popover](./popover.md) - Non-menu popover
|
|
667
|
-
- [ContextMenu](./context-menu.md) - Right-click menu
|
|
668
|
-
- [Select](./select.md) - Form select field
|