@hypoth-ui/cli 0.0.1 → 0.1.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/LICENSE +21 -0
- package/README.md +19 -115
- package/dist/{add-PDBC4JTE.js → add-V5PW73GC.js} +29 -17
- package/dist/{chunk-5LTQ2XVL.js → chunk-27CLUUVC.js} +0 -2
- package/dist/{chunk-YPKFYE45.js → chunk-NWIRSZUQ.js} +6 -13
- package/dist/{chunk-GJ6JOQ3Q.js → chunk-PBK72SJJ.js} +1 -1
- package/dist/{diff-BQEXG7HU.js → diff-776UATCA.js} +2 -2
- package/dist/index.js +5 -5
- package/dist/{init-7AZXYAPJ.js → init-GDU2PW7K.js} +10 -13
- package/dist/{list-X6ZLM2NQ.js → list-XDP5I537.js} +3 -3
- package/package.json +16 -12
- package/registry/components.json +1820 -206
- package/templates/accordion/index.tsx +266 -0
- package/templates/accordion/wc/accordion-content.ts +113 -0
- package/templates/accordion/wc/accordion-item.ts +111 -0
- package/templates/accordion/wc/accordion-trigger.ts +105 -0
- package/templates/accordion/wc/accordion.ts +213 -0
- package/templates/accordion/wc/index.ts +12 -0
- package/templates/alert/index.tsx +177 -0
- package/templates/alert/wc/alert.ts +167 -0
- package/templates/alert/wc/index.ts +1 -0
- package/templates/alert-dialog/index.tsx +360 -0
- package/templates/alert-dialog/wc/alert-dialog-action.ts +43 -0
- package/templates/alert-dialog/wc/alert-dialog-cancel.ts +43 -0
- package/templates/alert-dialog/wc/alert-dialog-content.ts +42 -0
- package/templates/alert-dialog/wc/alert-dialog-description.ts +34 -0
- package/templates/alert-dialog/wc/alert-dialog-footer.ts +25 -0
- package/templates/alert-dialog/wc/alert-dialog-header.ts +25 -0
- package/templates/alert-dialog/wc/alert-dialog-title.ts +34 -0
- package/templates/alert-dialog/wc/alert-dialog-trigger.ts +46 -0
- package/templates/alert-dialog/wc/alert-dialog.ts +302 -0
- package/templates/alert-dialog/wc/index.ts +13 -0
- package/templates/aspect-ratio/index.tsx +50 -0
- package/templates/aspect-ratio/wc/aspect-ratio.ts +78 -0
- package/templates/aspect-ratio/wc/index.ts +5 -0
- package/templates/avatar/avatar-group.tsx +88 -0
- package/templates/avatar/avatar.tsx +124 -0
- package/templates/avatar/index.tsx +33 -0
- package/templates/avatar/wc/avatar-group.ts +112 -0
- package/templates/avatar/wc/avatar.ts +184 -0
- package/templates/avatar/wc/index.ts +5 -0
- package/templates/badge/index.tsx +140 -0
- package/templates/badge/wc/badge.ts +119 -0
- package/templates/badge/wc/index.ts +9 -0
- package/templates/breadcrumb/index.tsx +157 -0
- package/templates/breadcrumb/wc/breadcrumb-item.ts +30 -0
- package/templates/breadcrumb/wc/breadcrumb-link.ts +70 -0
- package/templates/breadcrumb/wc/breadcrumb-list.ts +30 -0
- package/templates/breadcrumb/wc/breadcrumb-page.ts +32 -0
- package/templates/breadcrumb/wc/breadcrumb-separator.ts +31 -0
- package/templates/breadcrumb/wc/breadcrumb.ts +55 -0
- package/templates/breadcrumb/wc/index.ts +10 -0
- package/templates/button/button.tsx +119 -0
- package/templates/button/index.ts +1 -0
- package/templates/button/wc/button.ts +169 -0
- package/templates/calendar/index.tsx +149 -0
- package/templates/calendar/wc/calendar.ts +316 -0
- package/templates/calendar/wc/index.ts +4 -0
- package/templates/card/index.tsx +108 -0
- package/templates/card/wc/card-content.ts +25 -0
- package/templates/card/wc/card-footer.ts +25 -0
- package/templates/card/wc/card-header.ts +25 -0
- package/templates/card/wc/card.ts +43 -0
- package/templates/card/wc/index.ts +8 -0
- package/templates/checkbox/checkbox.tsx +85 -0
- package/templates/checkbox/wc/checkbox.ts +247 -0
- package/templates/collapsible/index.tsx +172 -0
- package/templates/collapsible/wc/collapsible-content.ts +97 -0
- package/templates/collapsible/wc/collapsible-trigger.ts +39 -0
- package/templates/collapsible/wc/collapsible.ts +143 -0
- package/templates/collapsible/wc/index.ts +7 -0
- package/templates/combobox/combobox-content.tsx +141 -0
- package/templates/combobox/combobox-context.ts +36 -0
- package/templates/combobox/combobox-empty.tsx +38 -0
- package/templates/combobox/combobox-input.tsx +159 -0
- package/templates/combobox/combobox-loading.tsx +38 -0
- package/templates/combobox/combobox-option.tsx +99 -0
- package/templates/combobox/combobox-root.tsx +207 -0
- package/templates/combobox/combobox-tag.tsx +62 -0
- package/templates/combobox/index.ts +62 -0
- package/templates/combobox/wc/combobox-content.ts +97 -0
- package/templates/combobox/wc/combobox-input.ts +134 -0
- package/templates/combobox/wc/combobox-option.ts +111 -0
- package/templates/combobox/wc/combobox-tag.ts +103 -0
- package/templates/combobox/wc/combobox.ts +981 -0
- package/templates/combobox/wc/index.ts +5 -0
- package/templates/command/index.tsx +279 -0
- package/templates/command/wc/command-empty.ts +24 -0
- package/templates/command/wc/command-group.ts +60 -0
- package/templates/command/wc/command-input.ts +136 -0
- package/templates/command/wc/command-item.ts +78 -0
- package/templates/command/wc/command-list.ts +103 -0
- package/templates/command/wc/command-loading.ts +24 -0
- package/templates/command/wc/command-separator.ts +23 -0
- package/templates/command/wc/command.ts +176 -0
- package/templates/context-menu/index.tsx +262 -0
- package/templates/context-menu/wc/context-menu-content.ts +41 -0
- package/templates/context-menu/wc/context-menu-item.ts +83 -0
- package/templates/context-menu/wc/context-menu-label.ts +30 -0
- package/templates/context-menu/wc/context-menu-separator.ts +28 -0
- package/templates/context-menu/wc/context-menu.ts +324 -0
- package/templates/context-menu/wc/index.ts +9 -0
- package/templates/data-table/index.tsx +263 -0
- package/templates/data-table/wc/data-table.ts +405 -0
- package/templates/data-table/wc/index.ts +10 -0
- package/templates/date-picker/date-picker-calendar.tsx +352 -0
- package/templates/date-picker/date-picker-content.tsx +121 -0
- package/templates/date-picker/date-picker-context.ts +46 -0
- package/templates/date-picker/date-picker-root.tsx +201 -0
- package/templates/date-picker/date-picker-trigger.tsx +95 -0
- package/templates/date-picker/index.ts +44 -0
- package/templates/date-picker/wc/date-picker-calendar.ts +457 -0
- package/templates/date-picker/wc/date-picker.ts +592 -0
- package/templates/date-picker/wc/date-utils.ts +467 -0
- package/templates/date-picker/wc/index.ts +3 -0
- package/templates/dialog/dialog-close.tsx +57 -0
- package/templates/dialog/dialog-content.tsx +106 -0
- package/templates/dialog/dialog-context.ts +24 -0
- package/templates/dialog/dialog-description.tsx +51 -0
- package/templates/dialog/dialog-root.tsx +104 -0
- package/templates/dialog/dialog-title.tsx +38 -0
- package/templates/dialog/dialog-trigger.tsx +94 -0
- package/templates/dialog/index.ts +52 -0
- package/templates/dialog/wc/dialog-content.ts +59 -0
- package/templates/dialog/wc/dialog-description.ts +58 -0
- package/templates/dialog/wc/dialog-title.ts +56 -0
- package/templates/dialog/wc/dialog.ts +411 -0
- package/templates/drawer/index.tsx +263 -0
- package/templates/drawer/wc/drawer-content.ts +150 -0
- package/templates/drawer/wc/drawer-description.ts +34 -0
- package/templates/drawer/wc/drawer-footer.ts +25 -0
- package/templates/drawer/wc/drawer-header.ts +25 -0
- package/templates/drawer/wc/drawer-title.ts +34 -0
- package/templates/drawer/wc/drawer.ts +348 -0
- package/templates/drawer/wc/index.ts +10 -0
- package/templates/dropdown-menu/index.tsx +454 -0
- package/templates/dropdown-menu/wc/dropdown-menu-checkbox-item.ts +93 -0
- package/templates/dropdown-menu/wc/dropdown-menu-content.ts +43 -0
- package/templates/dropdown-menu/wc/dropdown-menu-item.ts +85 -0
- package/templates/dropdown-menu/wc/dropdown-menu-label.ts +31 -0
- package/templates/dropdown-menu/wc/dropdown-menu-radio-group.ts +80 -0
- package/templates/dropdown-menu/wc/dropdown-menu-radio-item.ts +101 -0
- package/templates/dropdown-menu/wc/dropdown-menu-separator.ts +28 -0
- package/templates/dropdown-menu/wc/dropdown-menu.ts +358 -0
- package/templates/dropdown-menu/wc/index.ts +12 -0
- package/templates/field/field-description.tsx +39 -0
- package/templates/field/field-error.tsx +37 -0
- package/templates/field/field.tsx +46 -0
- package/templates/field/index.ts +4 -0
- package/templates/field/label.tsx +40 -0
- package/templates/field/wc/field-description.ts +42 -0
- package/templates/field/wc/field-error.ts +46 -0
- package/templates/field/wc/field.ts +210 -0
- package/templates/field/wc/label.ts +54 -0
- package/templates/file-upload/file-upload-context.ts +26 -0
- package/templates/file-upload/file-upload-dropzone.tsx +111 -0
- package/templates/file-upload/file-upload-input.tsx +86 -0
- package/templates/file-upload/file-upload-item.tsx +105 -0
- package/templates/file-upload/file-upload-root.tsx +115 -0
- package/templates/file-upload/index.ts +50 -0
- package/templates/file-upload/wc/file-upload.ts +380 -0
- package/templates/file-upload/wc/index.ts +1 -0
- package/templates/hover-card/index.tsx +203 -0
- package/templates/hover-card/wc/hover-card-content.ts +50 -0
- package/templates/hover-card/wc/hover-card.ts +382 -0
- package/templates/hover-card/wc/index.ts +6 -0
- package/templates/icon/icon.tsx +76 -0
- package/templates/icon/wc/icon-adapter.ts +108 -0
- package/templates/icon/wc/icon.ts +161 -0
- package/templates/input/input.tsx +130 -0
- package/templates/input/wc/input.ts +216 -0
- package/templates/layout/app-shell.tsx +177 -0
- package/templates/layout/box.tsx +53 -0
- package/templates/layout/center.tsx +42 -0
- package/templates/layout/container.tsx +43 -0
- package/templates/layout/flow.tsx +83 -0
- package/templates/layout/grid.tsx +79 -0
- package/templates/layout/index.ts +33 -0
- package/templates/layout/inline.tsx +16 -0
- package/templates/layout/page.tsx +43 -0
- package/templates/layout/section.tsx +39 -0
- package/templates/layout/spacer.tsx +30 -0
- package/templates/layout/split.tsx +47 -0
- package/templates/layout/stack.tsx +16 -0
- package/templates/layout/wc/app-shell.ts +58 -0
- package/templates/layout/wc/box.ts +117 -0
- package/templates/layout/wc/center.ts +78 -0
- package/templates/layout/wc/container.ts +77 -0
- package/templates/layout/wc/flow.ts +149 -0
- package/templates/layout/wc/footer.ts +57 -0
- package/templates/layout/wc/grid.ts +142 -0
- package/templates/layout/wc/header.ts +57 -0
- package/templates/layout/wc/index.ts +41 -0
- package/templates/layout/wc/main.ts +46 -0
- package/templates/layout/wc/page.ts +81 -0
- package/templates/layout/wc/section.ts +65 -0
- package/templates/layout/wc/spacer.ts +77 -0
- package/templates/layout/wc/split.ts +94 -0
- package/templates/layout/wc/wrap.ts +93 -0
- package/templates/layout/wrap.tsx +46 -0
- package/templates/link/link.tsx +109 -0
- package/templates/link/wc/link.ts +124 -0
- package/templates/list/index.tsx +55 -0
- package/templates/list/list-item.tsx +117 -0
- package/templates/list/list.tsx +115 -0
- package/templates/list/wc/index.ts +5 -0
- package/templates/list/wc/list-item.ts +127 -0
- package/templates/list/wc/list.ts +114 -0
- package/templates/menu/index.ts +49 -0
- package/templates/menu/menu-content.tsx +109 -0
- package/templates/menu/menu-context.ts +17 -0
- package/templates/menu/menu-item.tsx +108 -0
- package/templates/menu/menu-label.tsx +32 -0
- package/templates/menu/menu-root.tsx +108 -0
- package/templates/menu/menu-separator.tsx +24 -0
- package/templates/menu/menu-trigger.tsx +104 -0
- package/templates/menu/wc/menu-content.ts +67 -0
- package/templates/menu/wc/menu-item.ts +109 -0
- package/templates/menu/wc/menu.ts +449 -0
- package/templates/navigation-menu/index.tsx +328 -0
- package/templates/navigation-menu/wc/index.ts +12 -0
- package/templates/navigation-menu/wc/navigation-menu-content.ts +30 -0
- package/templates/navigation-menu/wc/navigation-menu-indicator.ts +30 -0
- package/templates/navigation-menu/wc/navigation-menu-item.ts +60 -0
- package/templates/navigation-menu/wc/navigation-menu-link.ts +97 -0
- package/templates/navigation-menu/wc/navigation-menu-list.ts +30 -0
- package/templates/navigation-menu/wc/navigation-menu-trigger.ts +110 -0
- package/templates/navigation-menu/wc/navigation-menu-viewport.ts +85 -0
- package/templates/navigation-menu/wc/navigation-menu.ts +272 -0
- package/templates/number-input/index.ts +46 -0
- package/templates/number-input/number-input-context.ts +38 -0
- package/templates/number-input/number-input-decrement.tsx +53 -0
- package/templates/number-input/number-input-field.tsx +93 -0
- package/templates/number-input/number-input-increment.tsx +53 -0
- package/templates/number-input/number-input-root.tsx +137 -0
- package/templates/number-input/wc/index.ts +1 -0
- package/templates/number-input/wc/number-input.ts +283 -0
- package/templates/pagination/index.tsx +198 -0
- package/templates/pagination/wc/index.ts +11 -0
- package/templates/pagination/wc/pagination-content.ts +30 -0
- package/templates/pagination/wc/pagination-ellipsis.ts +28 -0
- package/templates/pagination/wc/pagination-item.ts +30 -0
- package/templates/pagination/wc/pagination-link.ts +76 -0
- package/templates/pagination/wc/pagination-next.ts +69 -0
- package/templates/pagination/wc/pagination-previous.ts +69 -0
- package/templates/pagination/wc/pagination.ts +156 -0
- package/templates/pin-input/index.ts +39 -0
- package/templates/pin-input/pin-input-context.ts +30 -0
- package/templates/pin-input/pin-input-field.tsx +186 -0
- package/templates/pin-input/pin-input-root.tsx +120 -0
- package/templates/pin-input/wc/index.ts +1 -0
- package/templates/pin-input/wc/pin-input.ts +259 -0
- package/templates/popover/popover.tsx +121 -0
- package/templates/popover/wc/popover-content.ts +66 -0
- package/templates/popover/wc/popover.ts +343 -0
- package/templates/progress/index.tsx +117 -0
- package/templates/progress/wc/index.ts +4 -0
- package/templates/progress/wc/progress.ts +174 -0
- package/templates/radio/radio.tsx +43 -0
- package/templates/radio/wc/radio-group.ts +261 -0
- package/templates/radio/wc/radio.ts +145 -0
- package/templates/scroll-area/index.tsx +144 -0
- package/templates/scroll-area/wc/index.ts +8 -0
- package/templates/scroll-area/wc/scroll-area-scrollbar.ts +143 -0
- package/templates/scroll-area/wc/scroll-area-thumb.ts +225 -0
- package/templates/scroll-area/wc/scroll-area-viewport.ts +120 -0
- package/templates/scroll-area/wc/scroll-area.ts +63 -0
- package/templates/select/index.ts +57 -0
- package/templates/select/select-content.tsx +243 -0
- package/templates/select/select-context.ts +30 -0
- package/templates/select/select-group.tsx +53 -0
- package/templates/select/select-label.tsx +34 -0
- package/templates/select/select-option.tsx +97 -0
- package/templates/select/select-root.tsx +153 -0
- package/templates/select/select-separator.tsx +27 -0
- package/templates/select/select-trigger.tsx +112 -0
- package/templates/select/select-value.tsx +48 -0
- package/templates/select/wc/index.ts +6 -0
- package/templates/select/wc/select-content.ts +89 -0
- package/templates/select/wc/select-group.ts +82 -0
- package/templates/select/wc/select-label.ts +49 -0
- package/templates/select/wc/select-option.ts +111 -0
- package/templates/select/wc/select-trigger.ts +101 -0
- package/templates/select/wc/select.ts +840 -0
- package/templates/separator/index.tsx +49 -0
- package/templates/separator/wc/index.ts +5 -0
- package/templates/separator/wc/separator.ts +60 -0
- package/templates/sheet/index.tsx +291 -0
- package/templates/sheet/wc/index.ts +12 -0
- package/templates/sheet/wc/sheet-close.ts +43 -0
- package/templates/sheet/wc/sheet-content.ts +47 -0
- package/templates/sheet/wc/sheet-description.ts +34 -0
- package/templates/sheet/wc/sheet-footer.ts +25 -0
- package/templates/sheet/wc/sheet-header.ts +25 -0
- package/templates/sheet/wc/sheet-overlay.ts +23 -0
- package/templates/sheet/wc/sheet-title.ts +34 -0
- package/templates/sheet/wc/sheet.ts +336 -0
- package/templates/skeleton/index.tsx +131 -0
- package/templates/skeleton/wc/index.ts +10 -0
- package/templates/skeleton/wc/skeleton.ts +107 -0
- package/templates/slider/index.ts +41 -0
- package/templates/slider/slider-context.ts +36 -0
- package/templates/slider/slider-range.tsx +59 -0
- package/templates/slider/slider-root.tsx +166 -0
- package/templates/slider/slider-thumb.tsx +213 -0
- package/templates/slider/slider-track.tsx +113 -0
- package/templates/slider/wc/index.ts +1 -0
- package/templates/slider/wc/slider.ts +465 -0
- package/templates/spinner/spinner.tsx +64 -0
- package/templates/spinner/wc/spinner.ts +70 -0
- package/templates/stepper/index.tsx +230 -0
- package/templates/stepper/wc/index.ts +12 -0
- package/templates/stepper/wc/stepper-content.ts +30 -0
- package/templates/stepper/wc/stepper-description.ts +25 -0
- package/templates/stepper/wc/stepper-indicator.ts +30 -0
- package/templates/stepper/wc/stepper-item.ts +55 -0
- package/templates/stepper/wc/stepper-separator.ts +29 -0
- package/templates/stepper/wc/stepper-title.ts +25 -0
- package/templates/stepper/wc/stepper-trigger.ts +67 -0
- package/templates/stepper/wc/stepper.ts +164 -0
- package/templates/switch/switch.tsx +90 -0
- package/templates/switch/wc/switch.ts +228 -0
- package/templates/table/body.tsx +21 -0
- package/templates/table/cell.tsx +44 -0
- package/templates/table/head.tsx +112 -0
- package/templates/table/header.tsx +21 -0
- package/templates/table/index.tsx +93 -0
- package/templates/table/root.tsx +82 -0
- package/templates/table/row.tsx +36 -0
- package/templates/table/wc/index.ts +9 -0
- package/templates/table/wc/table-body.ts +32 -0
- package/templates/table/wc/table-cell.ts +58 -0
- package/templates/table/wc/table-head.ts +129 -0
- package/templates/table/wc/table-header.ts +32 -0
- package/templates/table/wc/table-row.ts +50 -0
- package/templates/table/wc/table.ts +93 -0
- package/templates/tabs/index.tsx +222 -0
- package/templates/tabs/wc/index.ts +8 -0
- package/templates/tabs/wc/tabs-content.ts +82 -0
- package/templates/tabs/wc/tabs-list.ts +56 -0
- package/templates/tabs/wc/tabs-trigger.ts +136 -0
- package/templates/tabs/wc/tabs.ts +202 -0
- package/templates/tag/index.tsx +186 -0
- package/templates/tag/wc/index.ts +4 -0
- package/templates/tag/wc/tag.ts +166 -0
- package/templates/text/text.tsx +100 -0
- package/templates/text/wc/text.ts +94 -0
- package/templates/textarea/textarea.tsx +134 -0
- package/templates/textarea/wc/textarea.ts +280 -0
- package/templates/time-picker/index.ts +42 -0
- package/templates/time-picker/time-picker-context.ts +28 -0
- package/templates/time-picker/time-picker-root.tsx +113 -0
- package/templates/time-picker/time-picker-segment.tsx +91 -0
- package/templates/time-picker/wc/index.ts +1 -0
- package/templates/time-picker/wc/time-picker.ts +221 -0
- package/templates/toast/index.tsx +71 -0
- package/templates/toast/provider.tsx +228 -0
- package/templates/toast/toast.tsx +142 -0
- package/templates/toast/use-toast.ts +89 -0
- package/templates/toast/wc/index.ts +15 -0
- package/templates/toast/wc/toast-controller.ts +282 -0
- package/templates/toast/wc/toast-provider.ts +161 -0
- package/templates/toast/wc/toast.ts +165 -0
- package/templates/tooltip/tooltip.tsx +62 -0
- package/templates/tooltip/wc/tooltip-content.ts +64 -0
- package/templates/tooltip/wc/tooltip.ts +289 -0
- package/templates/tree/index.tsx +60 -0
- package/templates/tree/tree-item.tsx +131 -0
- package/templates/tree/tree.tsx +138 -0
- package/templates/tree/wc/index.ts +11 -0
- package/templates/tree/wc/tree-item.ts +273 -0
- package/templates/tree/wc/tree-utils.ts +143 -0
- package/templates/tree/wc/tree.ts +139 -0
- package/templates/visually-hidden/visually-hidden.tsx +45 -0
- package/templates/visually-hidden/wc/visually-hidden.ts +64 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DropdownMenu compound component exports.
|
|
3
|
+
*
|
|
4
|
+
* DropdownMenu is used for action menus triggered by a button, with support
|
|
5
|
+
* for items, separators, labels, checkbox items, and radio groups.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { DropdownMenu } from "@/components/ui";
|
|
10
|
+
*
|
|
11
|
+
* <DropdownMenu.Root>
|
|
12
|
+
* <DropdownMenu.Trigger>
|
|
13
|
+
* <button>Actions</button>
|
|
14
|
+
* </DropdownMenu.Trigger>
|
|
15
|
+
* <DropdownMenu.Content>
|
|
16
|
+
* <DropdownMenu.Label>Actions</DropdownMenu.Label>
|
|
17
|
+
* <DropdownMenu.Item value="edit">Edit</DropdownMenu.Item>
|
|
18
|
+
* <DropdownMenu.Separator />
|
|
19
|
+
* <DropdownMenu.Item value="delete" variant="destructive">Delete</DropdownMenu.Item>
|
|
20
|
+
* </DropdownMenu.Content>
|
|
21
|
+
* </DropdownMenu.Root>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
type HTMLAttributes,
|
|
27
|
+
type ReactNode,
|
|
28
|
+
createElement,
|
|
29
|
+
forwardRef,
|
|
30
|
+
useCallback,
|
|
31
|
+
useEffect,
|
|
32
|
+
useRef,
|
|
33
|
+
useState,
|
|
34
|
+
} from "react";
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Types
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
export type DropdownMenuPlacement =
|
|
41
|
+
| "top"
|
|
42
|
+
| "top-start"
|
|
43
|
+
| "top-end"
|
|
44
|
+
| "bottom"
|
|
45
|
+
| "bottom-start"
|
|
46
|
+
| "bottom-end"
|
|
47
|
+
| "left"
|
|
48
|
+
| "left-start"
|
|
49
|
+
| "left-end"
|
|
50
|
+
| "right"
|
|
51
|
+
| "right-start"
|
|
52
|
+
| "right-end";
|
|
53
|
+
|
|
54
|
+
export type DropdownMenuItemVariant = "default" | "destructive";
|
|
55
|
+
|
|
56
|
+
export interface DropdownMenuRootProps extends HTMLAttributes<HTMLElement> {
|
|
57
|
+
/** Content */
|
|
58
|
+
children?: ReactNode;
|
|
59
|
+
/** Controlled open state */
|
|
60
|
+
open?: boolean;
|
|
61
|
+
/** Default open state (uncontrolled) */
|
|
62
|
+
defaultOpen?: boolean;
|
|
63
|
+
/** Called when open state changes */
|
|
64
|
+
onOpenChange?: (open: boolean) => void;
|
|
65
|
+
/** Placement relative to trigger */
|
|
66
|
+
placement?: DropdownMenuPlacement;
|
|
67
|
+
/** Offset distance from trigger in pixels */
|
|
68
|
+
offset?: number;
|
|
69
|
+
/** Whether to flip placement when near viewport edge */
|
|
70
|
+
flip?: boolean;
|
|
71
|
+
/** Whether to animate open/close transitions */
|
|
72
|
+
animated?: boolean;
|
|
73
|
+
/** Modal behavior - blocks interaction outside menu */
|
|
74
|
+
modal?: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface DropdownMenuTriggerProps extends HTMLAttributes<HTMLElement> {
|
|
78
|
+
/** Trigger content (typically a button) */
|
|
79
|
+
children?: ReactNode;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface DropdownMenuContentProps extends HTMLAttributes<HTMLElement> {
|
|
83
|
+
/** Content */
|
|
84
|
+
children?: ReactNode;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface DropdownMenuItemProps extends Omit<HTMLAttributes<HTMLElement>, "onSelect"> {
|
|
88
|
+
/** Item content */
|
|
89
|
+
children?: ReactNode;
|
|
90
|
+
/** Value for selection events */
|
|
91
|
+
value?: string;
|
|
92
|
+
/** Visual variant */
|
|
93
|
+
variant?: DropdownMenuItemVariant;
|
|
94
|
+
/** Disabled state */
|
|
95
|
+
disabled?: boolean;
|
|
96
|
+
/** Called when item is selected */
|
|
97
|
+
onSelect?: (value: string) => void;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface DropdownMenuSeparatorProps extends HTMLAttributes<HTMLElement> {}
|
|
101
|
+
|
|
102
|
+
export interface DropdownMenuLabelProps extends HTMLAttributes<HTMLElement> {
|
|
103
|
+
/** Label content */
|
|
104
|
+
children?: ReactNode;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface DropdownMenuCheckboxItemProps extends HTMLAttributes<HTMLElement> {
|
|
108
|
+
/** Item content */
|
|
109
|
+
children?: ReactNode;
|
|
110
|
+
/** Whether the checkbox is checked */
|
|
111
|
+
checked?: boolean;
|
|
112
|
+
/** Called when checkbox state changes */
|
|
113
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
114
|
+
/** Disabled state */
|
|
115
|
+
disabled?: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface DropdownMenuRadioGroupProps extends HTMLAttributes<HTMLElement> {
|
|
119
|
+
/** Radio items */
|
|
120
|
+
children?: ReactNode;
|
|
121
|
+
/** Currently selected value */
|
|
122
|
+
value?: string;
|
|
123
|
+
/** Called when selection changes */
|
|
124
|
+
onValueChange?: (value: string) => void;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface DropdownMenuRadioItemProps extends HTMLAttributes<HTMLElement> {
|
|
128
|
+
/** Item content */
|
|
129
|
+
children?: ReactNode;
|
|
130
|
+
/** Value for this radio item */
|
|
131
|
+
value: string;
|
|
132
|
+
/** Disabled state */
|
|
133
|
+
disabled?: boolean;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// Components
|
|
138
|
+
// ============================================================================
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* DropdownMenu root component.
|
|
142
|
+
*/
|
|
143
|
+
const DropdownMenuRoot = forwardRef<HTMLElement, DropdownMenuRootProps>(function DropdownMenuRoot(
|
|
144
|
+
{
|
|
145
|
+
children,
|
|
146
|
+
className,
|
|
147
|
+
open: controlledOpen,
|
|
148
|
+
defaultOpen = false,
|
|
149
|
+
onOpenChange,
|
|
150
|
+
placement = "bottom-start",
|
|
151
|
+
offset = 4,
|
|
152
|
+
flip = true,
|
|
153
|
+
animated = true,
|
|
154
|
+
modal = true,
|
|
155
|
+
...props
|
|
156
|
+
},
|
|
157
|
+
ref
|
|
158
|
+
) {
|
|
159
|
+
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
160
|
+
const isControlled = controlledOpen !== undefined;
|
|
161
|
+
const open = isControlled ? controlledOpen : internalOpen;
|
|
162
|
+
const elementRef = useRef<HTMLElement>(null);
|
|
163
|
+
|
|
164
|
+
// Combine refs
|
|
165
|
+
const combinedRef = (node: HTMLElement | null) => {
|
|
166
|
+
(elementRef as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
167
|
+
if (typeof ref === "function") {
|
|
168
|
+
ref(node);
|
|
169
|
+
} else if (ref) {
|
|
170
|
+
(ref as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const handleOpenChange = useCallback(
|
|
175
|
+
(event: Event) => {
|
|
176
|
+
const customEvent = event as CustomEvent;
|
|
177
|
+
const isOpen = customEvent.type === "ds:open";
|
|
178
|
+
|
|
179
|
+
if (!isControlled) {
|
|
180
|
+
setInternalOpen(isOpen);
|
|
181
|
+
}
|
|
182
|
+
onOpenChange?.(isOpen);
|
|
183
|
+
},
|
|
184
|
+
[isControlled, onOpenChange]
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// Attach event listeners
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
const element = elementRef.current;
|
|
190
|
+
if (!element) return;
|
|
191
|
+
|
|
192
|
+
element.addEventListener("ds:open", handleOpenChange);
|
|
193
|
+
element.addEventListener("ds:close", handleOpenChange);
|
|
194
|
+
|
|
195
|
+
return () => {
|
|
196
|
+
element.removeEventListener("ds:open", handleOpenChange);
|
|
197
|
+
element.removeEventListener("ds:close", handleOpenChange);
|
|
198
|
+
};
|
|
199
|
+
}, [handleOpenChange]);
|
|
200
|
+
|
|
201
|
+
return createElement(
|
|
202
|
+
"ds-dropdown-menu",
|
|
203
|
+
{
|
|
204
|
+
ref: combinedRef,
|
|
205
|
+
class: className,
|
|
206
|
+
open: open || undefined,
|
|
207
|
+
placement,
|
|
208
|
+
offset,
|
|
209
|
+
flip: flip || undefined,
|
|
210
|
+
animated: animated || undefined,
|
|
211
|
+
modal: modal || undefined,
|
|
212
|
+
...props,
|
|
213
|
+
},
|
|
214
|
+
children
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
DropdownMenuRoot.displayName = "DropdownMenu.Root";
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* DropdownMenu trigger component.
|
|
221
|
+
*/
|
|
222
|
+
const DropdownMenuTrigger = forwardRef<HTMLElement, DropdownMenuTriggerProps>(
|
|
223
|
+
function DropdownMenuTrigger({ children, className, ...props }, ref) {
|
|
224
|
+
return createElement("span", { ref, className, slot: "trigger", ...props }, children);
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
DropdownMenuTrigger.displayName = "DropdownMenu.Trigger";
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* DropdownMenu content component.
|
|
231
|
+
*/
|
|
232
|
+
const DropdownMenuContent = forwardRef<HTMLElement, DropdownMenuContentProps>(
|
|
233
|
+
function DropdownMenuContent({ children, className, ...props }, ref) {
|
|
234
|
+
return createElement("ds-dropdown-menu-content", { ref, class: className, ...props }, children);
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
DropdownMenuContent.displayName = "DropdownMenu.Content";
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* DropdownMenu item component.
|
|
241
|
+
*/
|
|
242
|
+
const DropdownMenuItem = forwardRef<HTMLElement, DropdownMenuItemProps>(function DropdownMenuItem(
|
|
243
|
+
{ children, className, value, variant = "default", disabled = false, onSelect, ...props },
|
|
244
|
+
ref
|
|
245
|
+
) {
|
|
246
|
+
const elementRef = useRef<HTMLElement>(null);
|
|
247
|
+
|
|
248
|
+
// Combine refs
|
|
249
|
+
const combinedRef = (node: HTMLElement | null) => {
|
|
250
|
+
(elementRef as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
251
|
+
if (typeof ref === "function") {
|
|
252
|
+
ref(node);
|
|
253
|
+
} else if (ref) {
|
|
254
|
+
(ref as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// Attach select handler
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
const element = elementRef.current;
|
|
261
|
+
if (!element || !onSelect) return;
|
|
262
|
+
|
|
263
|
+
const handleSelect = (event: Event) => {
|
|
264
|
+
const customEvent = event as CustomEvent<{ value: string }>;
|
|
265
|
+
onSelect(customEvent.detail.value);
|
|
266
|
+
};
|
|
267
|
+
element.addEventListener("ds:select", handleSelect);
|
|
268
|
+
|
|
269
|
+
return () => {
|
|
270
|
+
element.removeEventListener("ds:select", handleSelect);
|
|
271
|
+
};
|
|
272
|
+
}, [onSelect]);
|
|
273
|
+
|
|
274
|
+
return createElement(
|
|
275
|
+
"ds-dropdown-menu-item",
|
|
276
|
+
{
|
|
277
|
+
ref: combinedRef,
|
|
278
|
+
class: className,
|
|
279
|
+
value,
|
|
280
|
+
variant,
|
|
281
|
+
disabled: disabled || undefined,
|
|
282
|
+
...props,
|
|
283
|
+
},
|
|
284
|
+
children
|
|
285
|
+
);
|
|
286
|
+
});
|
|
287
|
+
DropdownMenuItem.displayName = "DropdownMenu.Item";
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* DropdownMenu separator component.
|
|
291
|
+
*/
|
|
292
|
+
const DropdownMenuSeparator = forwardRef<HTMLElement, DropdownMenuSeparatorProps>(
|
|
293
|
+
function DropdownMenuSeparator({ className, ...props }, ref) {
|
|
294
|
+
return createElement("ds-dropdown-menu-separator", { ref, class: className, ...props });
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
DropdownMenuSeparator.displayName = "DropdownMenu.Separator";
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* DropdownMenu label component.
|
|
301
|
+
*/
|
|
302
|
+
const DropdownMenuLabel = forwardRef<HTMLElement, DropdownMenuLabelProps>(
|
|
303
|
+
function DropdownMenuLabel({ children, className, ...props }, ref) {
|
|
304
|
+
return createElement("ds-dropdown-menu-label", { ref, class: className, ...props }, children);
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
DropdownMenuLabel.displayName = "DropdownMenu.Label";
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* DropdownMenu checkbox item component.
|
|
311
|
+
*/
|
|
312
|
+
const DropdownMenuCheckboxItem = forwardRef<HTMLElement, DropdownMenuCheckboxItemProps>(
|
|
313
|
+
function DropdownMenuCheckboxItem(
|
|
314
|
+
{ children, className, checked = false, onCheckedChange, disabled = false, ...props },
|
|
315
|
+
ref
|
|
316
|
+
) {
|
|
317
|
+
const elementRef = useRef<HTMLElement>(null);
|
|
318
|
+
|
|
319
|
+
// Combine refs
|
|
320
|
+
const combinedRef = (node: HTMLElement | null) => {
|
|
321
|
+
(elementRef as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
322
|
+
if (typeof ref === "function") {
|
|
323
|
+
ref(node);
|
|
324
|
+
} else if (ref) {
|
|
325
|
+
(ref as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// Attach change handler
|
|
330
|
+
useEffect(() => {
|
|
331
|
+
const element = elementRef.current;
|
|
332
|
+
if (!element || !onCheckedChange) return;
|
|
333
|
+
|
|
334
|
+
const handleSelect = (event: Event) => {
|
|
335
|
+
const customEvent = event as CustomEvent<{ checked: boolean }>;
|
|
336
|
+
onCheckedChange(customEvent.detail.checked);
|
|
337
|
+
};
|
|
338
|
+
element.addEventListener("ds:select", handleSelect);
|
|
339
|
+
|
|
340
|
+
return () => {
|
|
341
|
+
element.removeEventListener("ds:select", handleSelect);
|
|
342
|
+
};
|
|
343
|
+
}, [onCheckedChange]);
|
|
344
|
+
|
|
345
|
+
return createElement(
|
|
346
|
+
"ds-dropdown-menu-checkbox-item",
|
|
347
|
+
{
|
|
348
|
+
ref: combinedRef,
|
|
349
|
+
class: className,
|
|
350
|
+
checked: checked || undefined,
|
|
351
|
+
disabled: disabled || undefined,
|
|
352
|
+
...props,
|
|
353
|
+
},
|
|
354
|
+
children
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
);
|
|
358
|
+
DropdownMenuCheckboxItem.displayName = "DropdownMenu.CheckboxItem";
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* DropdownMenu radio group component.
|
|
362
|
+
*/
|
|
363
|
+
const DropdownMenuRadioGroup = forwardRef<HTMLElement, DropdownMenuRadioGroupProps>(
|
|
364
|
+
function DropdownMenuRadioGroup({ children, className, value, onValueChange, ...props }, ref) {
|
|
365
|
+
const elementRef = useRef<HTMLElement>(null);
|
|
366
|
+
|
|
367
|
+
// Combine refs
|
|
368
|
+
const combinedRef = (node: HTMLElement | null) => {
|
|
369
|
+
(elementRef as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
370
|
+
if (typeof ref === "function") {
|
|
371
|
+
ref(node);
|
|
372
|
+
} else if (ref) {
|
|
373
|
+
(ref as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
// Attach change handler
|
|
378
|
+
useEffect(() => {
|
|
379
|
+
const element = elementRef.current;
|
|
380
|
+
if (!element || !onValueChange) return;
|
|
381
|
+
|
|
382
|
+
const handleChange = (event: Event) => {
|
|
383
|
+
const customEvent = event as CustomEvent<{ value: string }>;
|
|
384
|
+
onValueChange(customEvent.detail.value);
|
|
385
|
+
};
|
|
386
|
+
element.addEventListener("ds:change", handleChange);
|
|
387
|
+
|
|
388
|
+
return () => {
|
|
389
|
+
element.removeEventListener("ds:change", handleChange);
|
|
390
|
+
};
|
|
391
|
+
}, [onValueChange]);
|
|
392
|
+
|
|
393
|
+
return createElement(
|
|
394
|
+
"ds-dropdown-menu-radio-group",
|
|
395
|
+
{
|
|
396
|
+
ref: combinedRef,
|
|
397
|
+
class: className,
|
|
398
|
+
value,
|
|
399
|
+
...props,
|
|
400
|
+
},
|
|
401
|
+
children
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
);
|
|
405
|
+
DropdownMenuRadioGroup.displayName = "DropdownMenu.RadioGroup";
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* DropdownMenu radio item component.
|
|
409
|
+
*/
|
|
410
|
+
const DropdownMenuRadioItem = forwardRef<HTMLElement, DropdownMenuRadioItemProps>(
|
|
411
|
+
function DropdownMenuRadioItem({ children, className, value, disabled = false, ...props }, ref) {
|
|
412
|
+
return createElement(
|
|
413
|
+
"ds-dropdown-menu-radio-item",
|
|
414
|
+
{
|
|
415
|
+
ref,
|
|
416
|
+
class: className,
|
|
417
|
+
value,
|
|
418
|
+
disabled: disabled || undefined,
|
|
419
|
+
...props,
|
|
420
|
+
},
|
|
421
|
+
children
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
);
|
|
425
|
+
DropdownMenuRadioItem.displayName = "DropdownMenu.RadioItem";
|
|
426
|
+
|
|
427
|
+
// ============================================================================
|
|
428
|
+
// Compound Component
|
|
429
|
+
// ============================================================================
|
|
430
|
+
|
|
431
|
+
export const DropdownMenu = {
|
|
432
|
+
Root: DropdownMenuRoot,
|
|
433
|
+
Trigger: DropdownMenuTrigger,
|
|
434
|
+
Content: DropdownMenuContent,
|
|
435
|
+
Item: DropdownMenuItem,
|
|
436
|
+
Separator: DropdownMenuSeparator,
|
|
437
|
+
Label: DropdownMenuLabel,
|
|
438
|
+
CheckboxItem: DropdownMenuCheckboxItem,
|
|
439
|
+
RadioGroup: DropdownMenuRadioGroup,
|
|
440
|
+
RadioItem: DropdownMenuRadioItem,
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
// Also export individual components
|
|
444
|
+
export {
|
|
445
|
+
DropdownMenuRoot,
|
|
446
|
+
DropdownMenuTrigger,
|
|
447
|
+
DropdownMenuContent,
|
|
448
|
+
DropdownMenuItem,
|
|
449
|
+
DropdownMenuSeparator,
|
|
450
|
+
DropdownMenuLabel,
|
|
451
|
+
DropdownMenuCheckboxItem,
|
|
452
|
+
DropdownMenuRadioGroup,
|
|
453
|
+
DropdownMenuRadioItem,
|
|
454
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DropdownMenuCheckboxItem component - toggleable checkbox menu item.
|
|
3
|
+
*
|
|
4
|
+
* @element ds-dropdown-menu-checkbox-item
|
|
5
|
+
*
|
|
6
|
+
* @slot - Item content
|
|
7
|
+
*
|
|
8
|
+
* @fires ds:select - Fired when item is toggled
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { html } from "lit";
|
|
12
|
+
import { property } from "lit/decorators.js";
|
|
13
|
+
import { DSElement } from "../../base/ds-element.js";
|
|
14
|
+
import { emitEvent } from "../../events/emit.js";
|
|
15
|
+
import { define } from "../../registry/define.js";
|
|
16
|
+
|
|
17
|
+
export class DsDropdownMenuCheckboxItem extends DSElement {
|
|
18
|
+
/** Whether the checkbox is checked */
|
|
19
|
+
@property({ type: Boolean, reflect: true })
|
|
20
|
+
checked = false;
|
|
21
|
+
|
|
22
|
+
/** Disabled state */
|
|
23
|
+
@property({ type: Boolean, reflect: true })
|
|
24
|
+
disabled = false;
|
|
25
|
+
|
|
26
|
+
override connectedCallback(): void {
|
|
27
|
+
super.connectedCallback();
|
|
28
|
+
|
|
29
|
+
this.setAttribute("role", "menuitemcheckbox");
|
|
30
|
+
this.setAttribute("tabindex", "-1");
|
|
31
|
+
this.updateAriaChecked();
|
|
32
|
+
|
|
33
|
+
this.addEventListener("click", this.handleToggle);
|
|
34
|
+
this.addEventListener("keydown", this.handleKeyDown);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
override disconnectedCallback(): void {
|
|
38
|
+
super.disconnectedCallback();
|
|
39
|
+
this.removeEventListener("click", this.handleToggle);
|
|
40
|
+
this.removeEventListener("keydown", this.handleKeyDown);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private handleToggle = (): void => {
|
|
44
|
+
if (this.disabled) return;
|
|
45
|
+
|
|
46
|
+
this.checked = !this.checked;
|
|
47
|
+
this.updateAriaChecked();
|
|
48
|
+
|
|
49
|
+
emitEvent(this, "ds:select", {
|
|
50
|
+
detail: { checked: this.checked },
|
|
51
|
+
bubbles: true,
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
private handleKeyDown = (event: KeyboardEvent): void => {
|
|
56
|
+
if (this.disabled) return;
|
|
57
|
+
|
|
58
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
59
|
+
event.preventDefault();
|
|
60
|
+
this.handleToggle();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
private updateAriaChecked(): void {
|
|
65
|
+
this.setAttribute("aria-checked", String(this.checked));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override updated(changedProperties: Map<string, unknown>): void {
|
|
69
|
+
if (changedProperties.has("disabled")) {
|
|
70
|
+
this.setAttribute("aria-disabled", String(this.disabled));
|
|
71
|
+
}
|
|
72
|
+
if (changedProperties.has("checked")) {
|
|
73
|
+
this.updateAriaChecked();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override render() {
|
|
78
|
+
return html`
|
|
79
|
+
<span class="ds-dropdown-menu-checkbox-item__indicator" aria-hidden="true">
|
|
80
|
+
${this.checked ? "✓" : ""}
|
|
81
|
+
</span>
|
|
82
|
+
<slot></slot>
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
define("ds-dropdown-menu-checkbox-item", DsDropdownMenuCheckboxItem);
|
|
88
|
+
|
|
89
|
+
declare global {
|
|
90
|
+
interface HTMLElementTagNameMap {
|
|
91
|
+
"ds-dropdown-menu-checkbox-item": DsDropdownMenuCheckboxItem;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DropdownMenuContent component - container for menu items.
|
|
3
|
+
*
|
|
4
|
+
* @element ds-dropdown-menu-content
|
|
5
|
+
*
|
|
6
|
+
* @slot - Menu items
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { html } from "lit";
|
|
10
|
+
import { property } from "lit/decorators.js";
|
|
11
|
+
import { DSElement } from "../../base/ds-element.js";
|
|
12
|
+
import { define } from "../../registry/define.js";
|
|
13
|
+
|
|
14
|
+
export class DsDropdownMenuContent extends DSElement {
|
|
15
|
+
/** Data state for animations */
|
|
16
|
+
@property({ attribute: "data-state", reflect: true })
|
|
17
|
+
dataState: "open" | "closed" = "closed";
|
|
18
|
+
|
|
19
|
+
override connectedCallback(): void {
|
|
20
|
+
super.connectedCallback();
|
|
21
|
+
|
|
22
|
+
// Generate ID for ARIA
|
|
23
|
+
if (!this.id) {
|
|
24
|
+
this.id = `dropdown-menu-content-${crypto.randomUUID().slice(0, 8)}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Set ARIA role
|
|
28
|
+
this.setAttribute("role", "menu");
|
|
29
|
+
this.setAttribute("hidden", "");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override render() {
|
|
33
|
+
return html`<slot></slot>`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
define("ds-dropdown-menu-content", DsDropdownMenuContent);
|
|
38
|
+
|
|
39
|
+
declare global {
|
|
40
|
+
interface HTMLElementTagNameMap {
|
|
41
|
+
"ds-dropdown-menu-content": DsDropdownMenuContent;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DropdownMenuItem component - selectable menu item.
|
|
3
|
+
*
|
|
4
|
+
* @element ds-dropdown-menu-item
|
|
5
|
+
*
|
|
6
|
+
* @slot - Item content
|
|
7
|
+
*
|
|
8
|
+
* @fires ds:select - Fired when item is selected
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { html } from "lit";
|
|
12
|
+
import { property } from "lit/decorators.js";
|
|
13
|
+
import { DSElement } from "../../base/ds-element.js";
|
|
14
|
+
import { emitEvent } from "../../events/emit.js";
|
|
15
|
+
import { define } from "../../registry/define.js";
|
|
16
|
+
|
|
17
|
+
export type DropdownMenuItemVariant = "default" | "destructive";
|
|
18
|
+
|
|
19
|
+
export class DsDropdownMenuItem extends DSElement {
|
|
20
|
+
/** Value for selection events */
|
|
21
|
+
@property()
|
|
22
|
+
value = "";
|
|
23
|
+
|
|
24
|
+
/** Visual variant */
|
|
25
|
+
@property({ reflect: true })
|
|
26
|
+
variant: DropdownMenuItemVariant = "default";
|
|
27
|
+
|
|
28
|
+
/** Disabled state */
|
|
29
|
+
@property({ type: Boolean, reflect: true })
|
|
30
|
+
disabled = false;
|
|
31
|
+
|
|
32
|
+
override connectedCallback(): void {
|
|
33
|
+
super.connectedCallback();
|
|
34
|
+
|
|
35
|
+
// Set ARIA role
|
|
36
|
+
this.setAttribute("role", "menuitem");
|
|
37
|
+
this.setAttribute("tabindex", "-1");
|
|
38
|
+
|
|
39
|
+
// Listen for selection triggers
|
|
40
|
+
this.addEventListener("click", this.handleSelect);
|
|
41
|
+
this.addEventListener("keydown", this.handleKeyDown);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override disconnectedCallback(): void {
|
|
45
|
+
super.disconnectedCallback();
|
|
46
|
+
this.removeEventListener("click", this.handleSelect);
|
|
47
|
+
this.removeEventListener("keydown", this.handleKeyDown);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private handleSelect = (): void => {
|
|
51
|
+
if (this.disabled) return;
|
|
52
|
+
|
|
53
|
+
emitEvent(this, "ds:select", {
|
|
54
|
+
detail: { value: this.value },
|
|
55
|
+
bubbles: true,
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
private handleKeyDown = (event: KeyboardEvent): void => {
|
|
60
|
+
if (this.disabled) return;
|
|
61
|
+
|
|
62
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
this.handleSelect();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
override updated(changedProperties: Map<string, unknown>): void {
|
|
69
|
+
if (changedProperties.has("disabled")) {
|
|
70
|
+
this.setAttribute("aria-disabled", String(this.disabled));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
override render() {
|
|
75
|
+
return html`<slot></slot>`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
define("ds-dropdown-menu-item", DsDropdownMenuItem);
|
|
80
|
+
|
|
81
|
+
declare global {
|
|
82
|
+
interface HTMLElementTagNameMap {
|
|
83
|
+
"ds-dropdown-menu-item": DsDropdownMenuItem;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DropdownMenuLabel component - non-interactive label for menu sections.
|
|
3
|
+
*
|
|
4
|
+
* @element ds-dropdown-menu-label
|
|
5
|
+
*
|
|
6
|
+
* @slot - Label content
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { html } from "lit";
|
|
10
|
+
import { DSElement } from "../../base/ds-element.js";
|
|
11
|
+
import { define } from "../../registry/define.js";
|
|
12
|
+
|
|
13
|
+
export class DsDropdownMenuLabel extends DSElement {
|
|
14
|
+
override connectedCallback(): void {
|
|
15
|
+
super.connectedCallback();
|
|
16
|
+
// Labels are not focusable
|
|
17
|
+
this.setAttribute("aria-hidden", "true");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
override render() {
|
|
21
|
+
return html`<slot></slot>`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
define("ds-dropdown-menu-label", DsDropdownMenuLabel);
|
|
26
|
+
|
|
27
|
+
declare global {
|
|
28
|
+
interface HTMLElementTagNameMap {
|
|
29
|
+
"ds-dropdown-menu-label": DsDropdownMenuLabel;
|
|
30
|
+
}
|
|
31
|
+
}
|