@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,67 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import { property } from "lit/decorators.js";
|
|
3
|
+
import { DSElement } from "../../base/ds-element.js";
|
|
4
|
+
import { define } from "../../registry/define.js";
|
|
5
|
+
|
|
6
|
+
export type MenuContentState = "open" | "closed";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Menu content container with role="menu".
|
|
10
|
+
*
|
|
11
|
+
* @element ds-menu-content
|
|
12
|
+
*
|
|
13
|
+
* @slot - Menu items (ds-menu-item elements)
|
|
14
|
+
*
|
|
15
|
+
* @attr {string} data-state - Animation state ("open" or "closed")
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```html
|
|
19
|
+
* <ds-menu>
|
|
20
|
+
* <button slot="trigger">Actions</button>
|
|
21
|
+
* <ds-menu-content>
|
|
22
|
+
* <ds-menu-item>Edit</ds-menu-item>
|
|
23
|
+
* <ds-menu-item>Delete</ds-menu-item>
|
|
24
|
+
* </ds-menu-content>
|
|
25
|
+
* </ds-menu>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class DsMenuContent extends DSElement {
|
|
29
|
+
/** Unique ID for ARIA association */
|
|
30
|
+
@property({ type: String, reflect: true })
|
|
31
|
+
override id = "";
|
|
32
|
+
|
|
33
|
+
/** Animation state (open or closed) - set by parent ds-menu */
|
|
34
|
+
@property({ type: String, reflect: true, attribute: "data-state" })
|
|
35
|
+
dataState: MenuContentState = "closed";
|
|
36
|
+
|
|
37
|
+
override connectedCallback(): void {
|
|
38
|
+
super.connectedCallback();
|
|
39
|
+
|
|
40
|
+
// Generate ID if not set
|
|
41
|
+
if (!this.id) {
|
|
42
|
+
this.id = `menu-content-${crypto.randomUUID().slice(0, 8)}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Set ARIA role for menu
|
|
46
|
+
this.setAttribute("role", "menu");
|
|
47
|
+
|
|
48
|
+
// Hidden by default (parent menu controls visibility)
|
|
49
|
+
this.setAttribute("hidden", "");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
override render() {
|
|
53
|
+
return html`
|
|
54
|
+
<div class="ds-menu-content" part="container">
|
|
55
|
+
<slot></slot>
|
|
56
|
+
</div>
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
define("ds-menu-content", DsMenuContent);
|
|
62
|
+
|
|
63
|
+
declare global {
|
|
64
|
+
interface HTMLElementTagNameMap {
|
|
65
|
+
"ds-menu-content": DsMenuContent;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import { property } from "lit/decorators.js";
|
|
3
|
+
import { DSElement } from "../../base/ds-element.js";
|
|
4
|
+
import { emitEvent } from "../../events/emit.js";
|
|
5
|
+
import { define } from "../../registry/define.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Menu item with role="menuitem".
|
|
9
|
+
*
|
|
10
|
+
* @element ds-menu-item
|
|
11
|
+
*
|
|
12
|
+
* @slot - Item content (text, icon, etc.)
|
|
13
|
+
*
|
|
14
|
+
* @fires ds:select - Fired when item is selected via click, Enter, or Space
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```html
|
|
18
|
+
* <ds-menu-item value="edit">Edit</ds-menu-item>
|
|
19
|
+
* <ds-menu-item value="delete" disabled>Delete</ds-menu-item>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export class DsMenuItem extends DSElement {
|
|
23
|
+
/** Whether the item is disabled */
|
|
24
|
+
@property({ type: Boolean, reflect: true })
|
|
25
|
+
disabled = false;
|
|
26
|
+
|
|
27
|
+
/** Value associated with this item (used in ds:select event) */
|
|
28
|
+
@property({ type: String })
|
|
29
|
+
value = "";
|
|
30
|
+
|
|
31
|
+
override connectedCallback(): void {
|
|
32
|
+
super.connectedCallback();
|
|
33
|
+
|
|
34
|
+
// Set ARIA role
|
|
35
|
+
this.setAttribute("role", "menuitem");
|
|
36
|
+
|
|
37
|
+
// Set tabIndex for roving focus (will be managed by parent)
|
|
38
|
+
this.tabIndex = -1;
|
|
39
|
+
|
|
40
|
+
// Update aria-disabled
|
|
41
|
+
this.updateAriaDisabled();
|
|
42
|
+
|
|
43
|
+
// Listen for activation
|
|
44
|
+
this.addEventListener("click", this.handleClick);
|
|
45
|
+
this.addEventListener("keydown", this.handleKeyDown);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override disconnectedCallback(): void {
|
|
49
|
+
super.disconnectedCallback();
|
|
50
|
+
this.removeEventListener("click", this.handleClick);
|
|
51
|
+
this.removeEventListener("keydown", this.handleKeyDown);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private updateAriaDisabled(): void {
|
|
55
|
+
if (this.disabled) {
|
|
56
|
+
this.setAttribute("aria-disabled", "true");
|
|
57
|
+
} else {
|
|
58
|
+
this.removeAttribute("aria-disabled");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private handleClick = (): void => {
|
|
63
|
+
this.select();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
private handleKeyDown = (event: KeyboardEvent): void => {
|
|
67
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
this.select();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Triggers selection of this item.
|
|
75
|
+
* Emits ds:select event and notifies parent menu to close.
|
|
76
|
+
*/
|
|
77
|
+
public select(): void {
|
|
78
|
+
if (this.disabled) return;
|
|
79
|
+
|
|
80
|
+
// Get value from attribute or text content
|
|
81
|
+
const value = this.value || this.textContent?.trim() || "";
|
|
82
|
+
|
|
83
|
+
emitEvent(this, "select", { detail: { value } });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
override updated(changedProperties: Map<string, unknown>): void {
|
|
87
|
+
super.updated(changedProperties);
|
|
88
|
+
|
|
89
|
+
if (changedProperties.has("disabled")) {
|
|
90
|
+
this.updateAriaDisabled();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
override render() {
|
|
95
|
+
return html`
|
|
96
|
+
<div class="ds-menu-item" part="container">
|
|
97
|
+
<slot></slot>
|
|
98
|
+
</div>
|
|
99
|
+
`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
define("ds-menu-item", DsMenuItem);
|
|
104
|
+
|
|
105
|
+
declare global {
|
|
106
|
+
interface HTMLElementTagNameMap {
|
|
107
|
+
"ds-menu-item": DsMenuItem;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AnchorPosition,
|
|
3
|
+
type DismissableLayer,
|
|
4
|
+
type Placement,
|
|
5
|
+
type Presence,
|
|
6
|
+
type RovingFocus,
|
|
7
|
+
type TypeAhead,
|
|
8
|
+
createAnchorPosition,
|
|
9
|
+
createDismissableLayer,
|
|
10
|
+
createPresence,
|
|
11
|
+
createRovingFocus,
|
|
12
|
+
createTypeAhead,
|
|
13
|
+
prefersReducedMotion,
|
|
14
|
+
} from "@hypoth-ui/primitives-dom";
|
|
15
|
+
import { html } from "lit";
|
|
16
|
+
import { property } from "lit/decorators.js";
|
|
17
|
+
import { DSElement } from "../../base/ds-element.js";
|
|
18
|
+
import { StandardEvents, emitEvent } from "../../events/emit.js";
|
|
19
|
+
import { define } from "../../registry/define.js";
|
|
20
|
+
|
|
21
|
+
// Import child components to ensure they're registered
|
|
22
|
+
import type { DsMenuContent } from "./menu-content.js";
|
|
23
|
+
import "./menu-content.js";
|
|
24
|
+
import "./menu-item.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Menu component with roving focus and type-ahead.
|
|
28
|
+
*
|
|
29
|
+
* Implements WAI-ARIA Menu Button pattern with:
|
|
30
|
+
* - Arrow key navigation between items
|
|
31
|
+
* - Type-ahead search to jump to items
|
|
32
|
+
* - Enter/Space/Click to select items
|
|
33
|
+
* - Escape to close
|
|
34
|
+
*
|
|
35
|
+
* @element ds-menu
|
|
36
|
+
*
|
|
37
|
+
* @slot trigger - Button that opens the menu
|
|
38
|
+
* @slot - Menu content (ds-menu-content with ds-menu-item children)
|
|
39
|
+
*
|
|
40
|
+
* @fires ds:open - Fired when menu opens
|
|
41
|
+
* @fires ds:close - Fired when menu closes
|
|
42
|
+
* @fires ds:select - Fired when a menu item is selected (bubbles from ds-menu-item)
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```html
|
|
46
|
+
* <ds-menu>
|
|
47
|
+
* <button slot="trigger">Actions</button>
|
|
48
|
+
* <ds-menu-content>
|
|
49
|
+
* <ds-menu-item value="edit">Edit</ds-menu-item>
|
|
50
|
+
* <ds-menu-item value="delete">Delete</ds-menu-item>
|
|
51
|
+
* </ds-menu-content>
|
|
52
|
+
* </ds-menu>
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export class DsMenu extends DSElement {
|
|
56
|
+
/** Whether the menu is open */
|
|
57
|
+
@property({ type: Boolean, reflect: true })
|
|
58
|
+
open = false;
|
|
59
|
+
|
|
60
|
+
/** Placement relative to trigger */
|
|
61
|
+
@property({ type: String, reflect: true })
|
|
62
|
+
placement: Placement = "bottom-start";
|
|
63
|
+
|
|
64
|
+
/** Offset distance from trigger in pixels */
|
|
65
|
+
@property({ type: Number })
|
|
66
|
+
offset = 8;
|
|
67
|
+
|
|
68
|
+
/** Whether to flip placement when near viewport edge */
|
|
69
|
+
@property({ type: Boolean })
|
|
70
|
+
flip = true;
|
|
71
|
+
|
|
72
|
+
/** Whether to animate open/close transitions */
|
|
73
|
+
@property({ type: Boolean })
|
|
74
|
+
animated = true;
|
|
75
|
+
|
|
76
|
+
private anchorPosition: AnchorPosition | null = null;
|
|
77
|
+
private dismissLayer: DismissableLayer | null = null;
|
|
78
|
+
private presence: Presence | null = null;
|
|
79
|
+
private rovingFocus: RovingFocus | null = null;
|
|
80
|
+
private typeAhead: TypeAhead | null = null;
|
|
81
|
+
private triggerElement: HTMLElement | null = null;
|
|
82
|
+
private resizeObserver: ResizeObserver | null = null;
|
|
83
|
+
private scrollHandler: (() => void) | null = null;
|
|
84
|
+
private focusFirstOnOpen: "first" | "last" | null = null;
|
|
85
|
+
|
|
86
|
+
override connectedCallback(): void {
|
|
87
|
+
super.connectedCallback();
|
|
88
|
+
|
|
89
|
+
// Listen for trigger interactions
|
|
90
|
+
this.addEventListener("click", this.handleTriggerClick);
|
|
91
|
+
this.addEventListener("keydown", this.handleTriggerKeyDown);
|
|
92
|
+
|
|
93
|
+
// Listen for item selection (bubbled from ds-menu-item)
|
|
94
|
+
this.addEventListener("ds:select", this.handleItemSelect);
|
|
95
|
+
|
|
96
|
+
// Setup after first render
|
|
97
|
+
this.updateComplete.then(() => {
|
|
98
|
+
this.setupTriggerAccessibility();
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
override disconnectedCallback(): void {
|
|
103
|
+
super.disconnectedCallback();
|
|
104
|
+
this.removeEventListener("click", this.handleTriggerClick);
|
|
105
|
+
this.removeEventListener("keydown", this.handleTriggerKeyDown);
|
|
106
|
+
this.removeEventListener("ds:select", this.handleItemSelect);
|
|
107
|
+
this.cleanup();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Opens the menu.
|
|
112
|
+
*/
|
|
113
|
+
public show(): void {
|
|
114
|
+
if (this.open) return;
|
|
115
|
+
|
|
116
|
+
this.open = true;
|
|
117
|
+
emitEvent(this, StandardEvents.OPEN);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Closes the menu.
|
|
122
|
+
*/
|
|
123
|
+
public close(): void {
|
|
124
|
+
if (!this.open) return;
|
|
125
|
+
|
|
126
|
+
const content = this.querySelector("ds-menu-content") as DsMenuContent | null;
|
|
127
|
+
|
|
128
|
+
// If animated, use presence for exit animation
|
|
129
|
+
if (this.animated && content && !prefersReducedMotion()) {
|
|
130
|
+
// Cleanup dismiss layer so it doesn't re-trigger
|
|
131
|
+
this.dismissLayer?.deactivate();
|
|
132
|
+
this.dismissLayer = null;
|
|
133
|
+
|
|
134
|
+
// Create presence for exit animation
|
|
135
|
+
this.presence = createPresence({
|
|
136
|
+
onExitComplete: () => {
|
|
137
|
+
this.completeClose();
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
this.presence.hide(content);
|
|
141
|
+
} else {
|
|
142
|
+
// No animation - close immediately
|
|
143
|
+
this.cleanup();
|
|
144
|
+
this.open = false;
|
|
145
|
+
emitEvent(this, StandardEvents.CLOSE);
|
|
146
|
+
|
|
147
|
+
// Return focus to trigger
|
|
148
|
+
this.triggerElement?.focus();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Completes the close after exit animation.
|
|
154
|
+
*/
|
|
155
|
+
private completeClose(): void {
|
|
156
|
+
this.cleanup();
|
|
157
|
+
this.open = false;
|
|
158
|
+
emitEvent(this, StandardEvents.CLOSE);
|
|
159
|
+
|
|
160
|
+
// Return focus to trigger
|
|
161
|
+
this.triggerElement?.focus();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Toggles the menu open/closed state.
|
|
166
|
+
*/
|
|
167
|
+
public toggle(): void {
|
|
168
|
+
if (this.open) {
|
|
169
|
+
this.close();
|
|
170
|
+
} else {
|
|
171
|
+
this.show();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private handleTriggerClick = (event: Event): void => {
|
|
176
|
+
const target = event.target as HTMLElement;
|
|
177
|
+
const trigger = target.closest('[slot="trigger"]');
|
|
178
|
+
|
|
179
|
+
if (trigger && this.contains(trigger)) {
|
|
180
|
+
event.preventDefault();
|
|
181
|
+
this.triggerElement = trigger as HTMLElement;
|
|
182
|
+
this.focusFirstOnOpen = "first";
|
|
183
|
+
this.toggle();
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
private handleTriggerKeyDown = (event: KeyboardEvent): void => {
|
|
188
|
+
const target = event.target as HTMLElement;
|
|
189
|
+
const trigger = target.closest('[slot="trigger"]');
|
|
190
|
+
|
|
191
|
+
if (!trigger || !this.contains(trigger)) return;
|
|
192
|
+
|
|
193
|
+
this.triggerElement = trigger as HTMLElement;
|
|
194
|
+
|
|
195
|
+
switch (event.key) {
|
|
196
|
+
case "Enter":
|
|
197
|
+
case " ":
|
|
198
|
+
event.preventDefault();
|
|
199
|
+
this.focusFirstOnOpen = "first";
|
|
200
|
+
this.toggle();
|
|
201
|
+
break;
|
|
202
|
+
case "ArrowDown":
|
|
203
|
+
event.preventDefault();
|
|
204
|
+
this.focusFirstOnOpen = "first";
|
|
205
|
+
if (!this.open) this.show();
|
|
206
|
+
break;
|
|
207
|
+
case "ArrowUp":
|
|
208
|
+
event.preventDefault();
|
|
209
|
+
this.focusFirstOnOpen = "last";
|
|
210
|
+
if (!this.open) this.show();
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
private handleItemSelect = (_event: Event): void => {
|
|
216
|
+
// Item was selected, close the menu
|
|
217
|
+
// Don't stop propagation - let the event bubble to consumers
|
|
218
|
+
this.close();
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
private handleDismiss = (): void => {
|
|
222
|
+
this.close();
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
private setupTriggerAccessibility(): void {
|
|
226
|
+
const trigger = this.querySelector('[slot="trigger"]');
|
|
227
|
+
const content = this.querySelector("ds-menu-content");
|
|
228
|
+
|
|
229
|
+
if (trigger && content) {
|
|
230
|
+
// Store reference
|
|
231
|
+
this.triggerElement = trigger as HTMLElement;
|
|
232
|
+
|
|
233
|
+
// Set ARIA attributes on trigger
|
|
234
|
+
trigger.setAttribute("aria-haspopup", "menu");
|
|
235
|
+
trigger.setAttribute("aria-expanded", String(this.open));
|
|
236
|
+
trigger.setAttribute("aria-controls", content.id);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private updateTriggerAria(): void {
|
|
241
|
+
const trigger = this.querySelector('[slot="trigger"]');
|
|
242
|
+
if (trigger) {
|
|
243
|
+
trigger.setAttribute("aria-expanded", String(this.open));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private setupPositioning(): void {
|
|
248
|
+
const trigger = this.querySelector('[slot="trigger"]') as HTMLElement | null;
|
|
249
|
+
const content = this.querySelector("ds-menu-content") as HTMLElement | null;
|
|
250
|
+
|
|
251
|
+
if (!trigger || !content) return;
|
|
252
|
+
|
|
253
|
+
// Setup anchor positioning
|
|
254
|
+
this.anchorPosition = createAnchorPosition({
|
|
255
|
+
anchor: trigger,
|
|
256
|
+
floating: content,
|
|
257
|
+
placement: this.placement,
|
|
258
|
+
offset: this.offset,
|
|
259
|
+
flip: this.flip,
|
|
260
|
+
onPositionChange: (pos) => {
|
|
261
|
+
content.setAttribute("data-placement", pos.placement);
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Setup resize observer for repositioning
|
|
266
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
267
|
+
this.anchorPosition?.update();
|
|
268
|
+
});
|
|
269
|
+
this.resizeObserver.observe(trigger);
|
|
270
|
+
this.resizeObserver.observe(content);
|
|
271
|
+
|
|
272
|
+
// Setup scroll handler for repositioning
|
|
273
|
+
this.scrollHandler = () => {
|
|
274
|
+
this.anchorPosition?.update();
|
|
275
|
+
};
|
|
276
|
+
window.addEventListener("scroll", this.scrollHandler, { passive: true });
|
|
277
|
+
window.addEventListener("resize", this.scrollHandler, { passive: true });
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private setupDismissLayer(): void {
|
|
281
|
+
const content = this.querySelector("ds-menu-content") as HTMLElement | null;
|
|
282
|
+
const trigger = this.querySelector('[slot="trigger"]') as HTMLElement | null;
|
|
283
|
+
|
|
284
|
+
if (!content) return;
|
|
285
|
+
|
|
286
|
+
this.dismissLayer = createDismissableLayer({
|
|
287
|
+
container: content,
|
|
288
|
+
excludeElements: trigger ? [trigger] : [],
|
|
289
|
+
onDismiss: this.handleDismiss,
|
|
290
|
+
closeOnEscape: true,
|
|
291
|
+
closeOnOutsideClick: true,
|
|
292
|
+
});
|
|
293
|
+
this.dismissLayer.activate();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private setupRovingFocus(): void {
|
|
297
|
+
const content = this.querySelector("ds-menu-content") as HTMLElement | null;
|
|
298
|
+
|
|
299
|
+
if (!content) return;
|
|
300
|
+
|
|
301
|
+
this.rovingFocus = createRovingFocus({
|
|
302
|
+
container: content,
|
|
303
|
+
selector: "ds-menu-item:not([disabled])",
|
|
304
|
+
direction: "vertical",
|
|
305
|
+
loop: true,
|
|
306
|
+
skipDisabled: true,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private setupTypeAhead(): void {
|
|
311
|
+
const content = this.querySelector("ds-menu-content") as HTMLElement | null;
|
|
312
|
+
|
|
313
|
+
if (!content) return;
|
|
314
|
+
|
|
315
|
+
this.typeAhead = createTypeAhead({
|
|
316
|
+
items: () =>
|
|
317
|
+
Array.from(content.querySelectorAll<HTMLElement>("ds-menu-item:not([disabled])")),
|
|
318
|
+
getText: (item) => item.textContent?.trim() || "",
|
|
319
|
+
onMatch: (_item, index) => {
|
|
320
|
+
this.rovingFocus?.setFocusedIndex(index);
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Wire type-ahead to keydown events
|
|
325
|
+
content.addEventListener("keydown", this.handleTypeAheadKeyDown);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private handleTypeAheadKeyDown = (event: KeyboardEvent): void => {
|
|
329
|
+
this.typeAhead?.handleKeyDown(event);
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
private focusInitialItem(): void {
|
|
333
|
+
const content = this.querySelector("ds-menu-content");
|
|
334
|
+
if (!content) return;
|
|
335
|
+
|
|
336
|
+
const items = content.querySelectorAll<HTMLElement>("ds-menu-item:not([disabled])");
|
|
337
|
+
if (items.length === 0) return;
|
|
338
|
+
|
|
339
|
+
if (this.focusFirstOnOpen === "last") {
|
|
340
|
+
// Focus last item (ArrowUp on trigger)
|
|
341
|
+
const lastIndex = items.length - 1;
|
|
342
|
+
this.rovingFocus?.setFocusedIndex(lastIndex);
|
|
343
|
+
} else {
|
|
344
|
+
// Focus first item (default)
|
|
345
|
+
this.rovingFocus?.setFocusedIndex(0);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
this.focusFirstOnOpen = null;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
private cleanup(): void {
|
|
352
|
+
const content = this.querySelector("ds-menu-content") as HTMLElement | null;
|
|
353
|
+
|
|
354
|
+
// Cleanup type-ahead listener
|
|
355
|
+
if (content) {
|
|
356
|
+
content.removeEventListener("keydown", this.handleTypeAheadKeyDown);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Cleanup anchor positioning
|
|
360
|
+
this.anchorPosition?.destroy();
|
|
361
|
+
this.anchorPosition = null;
|
|
362
|
+
|
|
363
|
+
// Cleanup dismiss layer
|
|
364
|
+
this.dismissLayer?.deactivate();
|
|
365
|
+
this.dismissLayer = null;
|
|
366
|
+
|
|
367
|
+
// Cleanup presence
|
|
368
|
+
this.presence?.destroy();
|
|
369
|
+
this.presence = null;
|
|
370
|
+
|
|
371
|
+
// Cleanup roving focus
|
|
372
|
+
this.rovingFocus?.destroy();
|
|
373
|
+
this.rovingFocus = null;
|
|
374
|
+
|
|
375
|
+
// Cleanup type-ahead
|
|
376
|
+
this.typeAhead?.reset();
|
|
377
|
+
this.typeAhead = null;
|
|
378
|
+
|
|
379
|
+
// Cleanup resize observer
|
|
380
|
+
this.resizeObserver?.disconnect();
|
|
381
|
+
this.resizeObserver = null;
|
|
382
|
+
|
|
383
|
+
// Cleanup scroll handler
|
|
384
|
+
if (this.scrollHandler) {
|
|
385
|
+
window.removeEventListener("scroll", this.scrollHandler);
|
|
386
|
+
window.removeEventListener("resize", this.scrollHandler);
|
|
387
|
+
this.scrollHandler = null;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
override async updated(changedProperties: Map<string, unknown>): Promise<void> {
|
|
392
|
+
super.updated(changedProperties);
|
|
393
|
+
|
|
394
|
+
if (changedProperties.has("open")) {
|
|
395
|
+
this.updateTriggerAria();
|
|
396
|
+
|
|
397
|
+
const content = this.querySelector("ds-menu-content") as DsMenuContent | null;
|
|
398
|
+
|
|
399
|
+
if (this.open) {
|
|
400
|
+
// Show content
|
|
401
|
+
content?.removeAttribute("hidden");
|
|
402
|
+
|
|
403
|
+
// Set data-state to open for entry animation
|
|
404
|
+
if (content) {
|
|
405
|
+
content.dataState = "open";
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Wait for DOM update
|
|
409
|
+
await this.updateComplete;
|
|
410
|
+
|
|
411
|
+
// Setup all behaviors
|
|
412
|
+
this.setupPositioning();
|
|
413
|
+
this.setupDismissLayer();
|
|
414
|
+
this.setupRovingFocus();
|
|
415
|
+
this.setupTypeAhead();
|
|
416
|
+
|
|
417
|
+
// Focus initial item
|
|
418
|
+
this.focusInitialItem();
|
|
419
|
+
} else {
|
|
420
|
+
// Hide content
|
|
421
|
+
content?.setAttribute("hidden", "");
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Update positioning if placement or offset changes while open
|
|
426
|
+
if (this.open && (changedProperties.has("placement") || changedProperties.has("offset"))) {
|
|
427
|
+
this.cleanup();
|
|
428
|
+
this.setupPositioning();
|
|
429
|
+
this.setupDismissLayer();
|
|
430
|
+
this.setupRovingFocus();
|
|
431
|
+
this.setupTypeAhead();
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
override render() {
|
|
436
|
+
return html`
|
|
437
|
+
<slot name="trigger"></slot>
|
|
438
|
+
<slot></slot>
|
|
439
|
+
`;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
define("ds-menu", DsMenu);
|
|
444
|
+
|
|
445
|
+
declare global {
|
|
446
|
+
interface HTMLElementTagNameMap {
|
|
447
|
+
"ds-menu": DsMenu;
|
|
448
|
+
}
|
|
449
|
+
}
|