@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,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HoverCard compound component exports.
|
|
3
|
+
*
|
|
4
|
+
* HoverCard displays rich preview content on hover with configurable delays.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { HoverCard } from "@/components/ui";
|
|
9
|
+
*
|
|
10
|
+
* <HoverCard.Root>
|
|
11
|
+
* <HoverCard.Trigger>
|
|
12
|
+
* <a href="/user/123">@johndoe</a>
|
|
13
|
+
* </HoverCard.Trigger>
|
|
14
|
+
* <HoverCard.Content>
|
|
15
|
+
* <img src="avatar.jpg" alt="" />
|
|
16
|
+
* <h3>John Doe</h3>
|
|
17
|
+
* <p>Software Engineer</p>
|
|
18
|
+
* </HoverCard.Content>
|
|
19
|
+
* </HoverCard.Root>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
type HTMLAttributes,
|
|
25
|
+
type ReactNode,
|
|
26
|
+
createElement,
|
|
27
|
+
forwardRef,
|
|
28
|
+
useCallback,
|
|
29
|
+
useEffect,
|
|
30
|
+
useRef,
|
|
31
|
+
useState,
|
|
32
|
+
} from "react";
|
|
33
|
+
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Types
|
|
36
|
+
// ============================================================================
|
|
37
|
+
|
|
38
|
+
export type HoverCardPlacement =
|
|
39
|
+
| "top"
|
|
40
|
+
| "top-start"
|
|
41
|
+
| "top-end"
|
|
42
|
+
| "bottom"
|
|
43
|
+
| "bottom-start"
|
|
44
|
+
| "bottom-end"
|
|
45
|
+
| "left"
|
|
46
|
+
| "left-start"
|
|
47
|
+
| "left-end"
|
|
48
|
+
| "right"
|
|
49
|
+
| "right-start"
|
|
50
|
+
| "right-end";
|
|
51
|
+
|
|
52
|
+
export interface HoverCardRootProps extends HTMLAttributes<HTMLElement> {
|
|
53
|
+
/** Content */
|
|
54
|
+
children?: ReactNode;
|
|
55
|
+
/** Controlled open state */
|
|
56
|
+
open?: boolean;
|
|
57
|
+
/** Default open state (uncontrolled) */
|
|
58
|
+
defaultOpen?: boolean;
|
|
59
|
+
/** Called when open state changes */
|
|
60
|
+
onOpenChange?: (open: boolean) => void;
|
|
61
|
+
/** Placement relative to trigger */
|
|
62
|
+
placement?: HoverCardPlacement;
|
|
63
|
+
/** Offset distance from trigger in pixels */
|
|
64
|
+
offset?: number;
|
|
65
|
+
/** Whether to flip placement when near viewport edge */
|
|
66
|
+
flip?: boolean;
|
|
67
|
+
/** Delay in ms before showing on hover */
|
|
68
|
+
openDelay?: number;
|
|
69
|
+
/** Delay in ms before hiding after hover leaves */
|
|
70
|
+
closeDelay?: number;
|
|
71
|
+
/** Whether to animate open/close transitions */
|
|
72
|
+
animated?: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface HoverCardTriggerProps extends HTMLAttributes<HTMLElement> {
|
|
76
|
+
/** Trigger content */
|
|
77
|
+
children?: ReactNode;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface HoverCardContentProps extends HTMLAttributes<HTMLElement> {
|
|
81
|
+
/** Content */
|
|
82
|
+
children?: ReactNode;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// Components
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* HoverCard root component.
|
|
91
|
+
*/
|
|
92
|
+
const HoverCardRoot = forwardRef<HTMLElement, HoverCardRootProps>(function HoverCardRoot(
|
|
93
|
+
{
|
|
94
|
+
children,
|
|
95
|
+
className,
|
|
96
|
+
open: controlledOpen,
|
|
97
|
+
defaultOpen = false,
|
|
98
|
+
onOpenChange,
|
|
99
|
+
placement = "bottom",
|
|
100
|
+
offset = 8,
|
|
101
|
+
flip = true,
|
|
102
|
+
openDelay = 700,
|
|
103
|
+
closeDelay = 300,
|
|
104
|
+
animated = true,
|
|
105
|
+
...props
|
|
106
|
+
},
|
|
107
|
+
ref
|
|
108
|
+
) {
|
|
109
|
+
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
110
|
+
const isControlled = controlledOpen !== undefined;
|
|
111
|
+
const open = isControlled ? controlledOpen : internalOpen;
|
|
112
|
+
const elementRef = useRef<HTMLElement>(null);
|
|
113
|
+
|
|
114
|
+
// Combine refs
|
|
115
|
+
const combinedRef = (node: HTMLElement | null) => {
|
|
116
|
+
(elementRef as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
117
|
+
if (typeof ref === "function") {
|
|
118
|
+
ref(node);
|
|
119
|
+
} else if (ref) {
|
|
120
|
+
(ref as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const handleOpenChange = useCallback(
|
|
125
|
+
(event: Event) => {
|
|
126
|
+
const customEvent = event as CustomEvent;
|
|
127
|
+
const isOpen = customEvent.type === "ds:open";
|
|
128
|
+
|
|
129
|
+
if (!isControlled) {
|
|
130
|
+
setInternalOpen(isOpen);
|
|
131
|
+
}
|
|
132
|
+
onOpenChange?.(isOpen);
|
|
133
|
+
},
|
|
134
|
+
[isControlled, onOpenChange]
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// Attach event listeners
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
const element = elementRef.current;
|
|
140
|
+
if (!element) return;
|
|
141
|
+
|
|
142
|
+
element.addEventListener("ds:open", handleOpenChange);
|
|
143
|
+
element.addEventListener("ds:close", handleOpenChange);
|
|
144
|
+
|
|
145
|
+
return () => {
|
|
146
|
+
element.removeEventListener("ds:open", handleOpenChange);
|
|
147
|
+
element.removeEventListener("ds:close", handleOpenChange);
|
|
148
|
+
};
|
|
149
|
+
}, [handleOpenChange]);
|
|
150
|
+
|
|
151
|
+
return createElement(
|
|
152
|
+
"ds-hover-card",
|
|
153
|
+
{
|
|
154
|
+
ref: combinedRef,
|
|
155
|
+
class: className,
|
|
156
|
+
open: open || undefined,
|
|
157
|
+
placement,
|
|
158
|
+
offset,
|
|
159
|
+
flip: flip || undefined,
|
|
160
|
+
"open-delay": openDelay,
|
|
161
|
+
"close-delay": closeDelay,
|
|
162
|
+
animated: animated || undefined,
|
|
163
|
+
...props,
|
|
164
|
+
},
|
|
165
|
+
children
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
HoverCardRoot.displayName = "HoverCard.Root";
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* HoverCard trigger component.
|
|
172
|
+
*/
|
|
173
|
+
const HoverCardTrigger = forwardRef<HTMLElement, HoverCardTriggerProps>(function HoverCardTrigger(
|
|
174
|
+
{ children, className, ...props },
|
|
175
|
+
ref
|
|
176
|
+
) {
|
|
177
|
+
return createElement("span", { ref, className, slot: "trigger", ...props }, children);
|
|
178
|
+
});
|
|
179
|
+
HoverCardTrigger.displayName = "HoverCard.Trigger";
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* HoverCard content component.
|
|
183
|
+
*/
|
|
184
|
+
const HoverCardContent = forwardRef<HTMLElement, HoverCardContentProps>(function HoverCardContent(
|
|
185
|
+
{ children, className, ...props },
|
|
186
|
+
ref
|
|
187
|
+
) {
|
|
188
|
+
return createElement("ds-hover-card-content", { ref, class: className, ...props }, children);
|
|
189
|
+
});
|
|
190
|
+
HoverCardContent.displayName = "HoverCard.Content";
|
|
191
|
+
|
|
192
|
+
// ============================================================================
|
|
193
|
+
// Compound Component
|
|
194
|
+
// ============================================================================
|
|
195
|
+
|
|
196
|
+
export const HoverCard = {
|
|
197
|
+
Root: HoverCardRoot,
|
|
198
|
+
Trigger: HoverCardTrigger,
|
|
199
|
+
Content: HoverCardContent,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Also export individual components
|
|
203
|
+
export { HoverCardRoot, HoverCardTrigger, HoverCardContent };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HoverCardContent component - container for hover card content.
|
|
3
|
+
*
|
|
4
|
+
* @element ds-hover-card-content
|
|
5
|
+
*
|
|
6
|
+
* @slot - Content to display in the hover card
|
|
7
|
+
*
|
|
8
|
+
* @attr {string} data-state - Animation state ("open" or "closed")
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { html } from "lit";
|
|
12
|
+
import { property } from "lit/decorators.js";
|
|
13
|
+
import { DSElement } from "../../base/ds-element.js";
|
|
14
|
+
import { define } from "../../registry/define.js";
|
|
15
|
+
|
|
16
|
+
export type HoverCardContentState = "open" | "closed";
|
|
17
|
+
|
|
18
|
+
export class DsHoverCardContent extends DSElement {
|
|
19
|
+
/** Unique ID for ARIA association */
|
|
20
|
+
@property({ type: String, reflect: true })
|
|
21
|
+
override id = "";
|
|
22
|
+
|
|
23
|
+
/** Animation state (open or closed) - set by parent */
|
|
24
|
+
@property({ type: String, reflect: true, attribute: "data-state" })
|
|
25
|
+
dataState: HoverCardContentState = "closed";
|
|
26
|
+
|
|
27
|
+
override connectedCallback(): void {
|
|
28
|
+
super.connectedCallback();
|
|
29
|
+
|
|
30
|
+
// Generate ID if not set
|
|
31
|
+
if (!this.id) {
|
|
32
|
+
this.id = `hover-card-content-${crypto.randomUUID().slice(0, 8)}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Hidden by default (parent controls visibility)
|
|
36
|
+
this.setAttribute("hidden", "");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
override render() {
|
|
40
|
+
return html`<slot></slot>`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
define("ds-hover-card-content", DsHoverCardContent);
|
|
45
|
+
|
|
46
|
+
declare global {
|
|
47
|
+
interface HTMLElementTagNameMap {
|
|
48
|
+
"ds-hover-card-content": DsHoverCardContent;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HoverCard component - rich preview card that appears on hover.
|
|
3
|
+
*
|
|
4
|
+
* Opens on mouseenter/focus with configurable delay, stays open when
|
|
5
|
+
* pointer moves to the card content.
|
|
6
|
+
*
|
|
7
|
+
* @element ds-hover-card
|
|
8
|
+
*
|
|
9
|
+
* @slot trigger - Element that triggers the hover card
|
|
10
|
+
* @slot - Hover card content (ds-hover-card-content)
|
|
11
|
+
*
|
|
12
|
+
* @fires ds:open - Fired when hover card opens
|
|
13
|
+
* @fires ds:close - Fired when hover card closes
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```html
|
|
17
|
+
* <ds-hover-card>
|
|
18
|
+
* <a slot="trigger" href="/user/123">@johndoe</a>
|
|
19
|
+
* <ds-hover-card-content>
|
|
20
|
+
* <img src="avatar.jpg" alt="">
|
|
21
|
+
* <h3>John Doe</h3>
|
|
22
|
+
* <p>Software Engineer</p>
|
|
23
|
+
* </ds-hover-card-content>
|
|
24
|
+
* </ds-hover-card>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
type AnchorPosition,
|
|
30
|
+
type DismissableLayer,
|
|
31
|
+
type Placement,
|
|
32
|
+
type Presence,
|
|
33
|
+
createAnchorPosition,
|
|
34
|
+
createDismissableLayer,
|
|
35
|
+
createPresence,
|
|
36
|
+
prefersReducedMotion,
|
|
37
|
+
} from "@hypoth-ui/primitives-dom";
|
|
38
|
+
import { html } from "lit";
|
|
39
|
+
import { property } from "lit/decorators.js";
|
|
40
|
+
import { DSElement } from "../../base/ds-element.js";
|
|
41
|
+
import { StandardEvents, emitEvent } from "../../events/emit.js";
|
|
42
|
+
import { define } from "../../registry/define.js";
|
|
43
|
+
|
|
44
|
+
// Import child component to ensure it's registered
|
|
45
|
+
import type { DsHoverCardContent } from "./hover-card-content.js";
|
|
46
|
+
import "./hover-card-content.js";
|
|
47
|
+
|
|
48
|
+
export class DsHoverCard extends DSElement {
|
|
49
|
+
/** Whether the hover card is open */
|
|
50
|
+
@property({ type: Boolean, reflect: true })
|
|
51
|
+
open = false;
|
|
52
|
+
|
|
53
|
+
/** Placement relative to trigger */
|
|
54
|
+
@property({ type: String, reflect: true })
|
|
55
|
+
placement: Placement = "bottom";
|
|
56
|
+
|
|
57
|
+
/** Offset distance from trigger in pixels */
|
|
58
|
+
@property({ type: Number })
|
|
59
|
+
offset = 8;
|
|
60
|
+
|
|
61
|
+
/** Whether to flip placement when near viewport edge */
|
|
62
|
+
@property({ type: Boolean })
|
|
63
|
+
flip = true;
|
|
64
|
+
|
|
65
|
+
/** Delay in ms before showing on hover */
|
|
66
|
+
@property({ type: Number, attribute: "open-delay" })
|
|
67
|
+
openDelay = 700;
|
|
68
|
+
|
|
69
|
+
/** Delay in ms before hiding after hover leaves */
|
|
70
|
+
@property({ type: Number, attribute: "close-delay" })
|
|
71
|
+
closeDelay = 300;
|
|
72
|
+
|
|
73
|
+
/** Whether to animate open/close transitions */
|
|
74
|
+
@property({ type: Boolean })
|
|
75
|
+
animated = true;
|
|
76
|
+
|
|
77
|
+
private anchorPosition: AnchorPosition | null = null;
|
|
78
|
+
private dismissLayer: DismissableLayer | null = null;
|
|
79
|
+
private presence: Presence | null = null;
|
|
80
|
+
private resizeObserver: ResizeObserver | null = null;
|
|
81
|
+
private scrollHandler: (() => void) | null = null;
|
|
82
|
+
private openTimer: ReturnType<typeof setTimeout> | null = null;
|
|
83
|
+
private closeTimer: ReturnType<typeof setTimeout> | null = null;
|
|
84
|
+
private isHoveringTrigger = false;
|
|
85
|
+
private isHoveringContent = false;
|
|
86
|
+
|
|
87
|
+
override connectedCallback(): void {
|
|
88
|
+
super.connectedCallback();
|
|
89
|
+
|
|
90
|
+
// Listen for hover/focus on trigger
|
|
91
|
+
this.addEventListener("mouseenter", this.handleTriggerMouseEnter, true);
|
|
92
|
+
this.addEventListener("mouseleave", this.handleTriggerMouseLeave, true);
|
|
93
|
+
this.addEventListener("focusin", this.handleFocusIn);
|
|
94
|
+
this.addEventListener("focusout", this.handleFocusOut);
|
|
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("mouseenter", this.handleTriggerMouseEnter, true);
|
|
105
|
+
this.removeEventListener("mouseleave", this.handleTriggerMouseLeave, true);
|
|
106
|
+
this.removeEventListener("focusin", this.handleFocusIn);
|
|
107
|
+
this.removeEventListener("focusout", this.handleFocusOut);
|
|
108
|
+
this.clearTimers();
|
|
109
|
+
this.cleanup();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Opens the hover card.
|
|
114
|
+
*/
|
|
115
|
+
public show(): void {
|
|
116
|
+
if (this.open) return;
|
|
117
|
+
|
|
118
|
+
this.clearTimers();
|
|
119
|
+
this.open = true;
|
|
120
|
+
emitEvent(this, StandardEvents.OPEN);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Closes the hover card.
|
|
125
|
+
*/
|
|
126
|
+
public close(): void {
|
|
127
|
+
if (!this.open) return;
|
|
128
|
+
|
|
129
|
+
this.clearTimers();
|
|
130
|
+
|
|
131
|
+
const content = this.querySelector("ds-hover-card-content") as DsHoverCardContent | null;
|
|
132
|
+
|
|
133
|
+
if (this.animated && content && !prefersReducedMotion()) {
|
|
134
|
+
this.dismissLayer?.deactivate();
|
|
135
|
+
this.dismissLayer = null;
|
|
136
|
+
|
|
137
|
+
this.presence = createPresence({
|
|
138
|
+
onExitComplete: () => {
|
|
139
|
+
this.completeClose();
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
this.presence.hide(content);
|
|
143
|
+
} else {
|
|
144
|
+
this.cleanup();
|
|
145
|
+
this.open = false;
|
|
146
|
+
emitEvent(this, StandardEvents.CLOSE);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private completeClose(): void {
|
|
151
|
+
this.cleanup();
|
|
152
|
+
this.open = false;
|
|
153
|
+
emitEvent(this, StandardEvents.CLOSE);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private clearTimers(): void {
|
|
157
|
+
if (this.openTimer) {
|
|
158
|
+
clearTimeout(this.openTimer);
|
|
159
|
+
this.openTimer = null;
|
|
160
|
+
}
|
|
161
|
+
if (this.closeTimer) {
|
|
162
|
+
clearTimeout(this.closeTimer);
|
|
163
|
+
this.closeTimer = null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private handleTriggerMouseEnter = (event: MouseEvent): void => {
|
|
168
|
+
const target = event.target as HTMLElement;
|
|
169
|
+
const trigger = target.closest('[slot="trigger"]');
|
|
170
|
+
const content = target.closest("ds-hover-card-content");
|
|
171
|
+
|
|
172
|
+
if (trigger && this.contains(trigger)) {
|
|
173
|
+
this.isHoveringTrigger = true;
|
|
174
|
+
this.scheduleOpen();
|
|
175
|
+
} else if (content && this.contains(content)) {
|
|
176
|
+
this.isHoveringContent = true;
|
|
177
|
+
this.cancelClose();
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
private handleTriggerMouseLeave = (event: MouseEvent): void => {
|
|
182
|
+
const target = event.target as HTMLElement;
|
|
183
|
+
const trigger = target.closest('[slot="trigger"]');
|
|
184
|
+
const content = target.closest("ds-hover-card-content");
|
|
185
|
+
|
|
186
|
+
if (trigger && this.contains(trigger)) {
|
|
187
|
+
this.isHoveringTrigger = false;
|
|
188
|
+
this.scheduleClose();
|
|
189
|
+
} else if (content && this.contains(content)) {
|
|
190
|
+
this.isHoveringContent = false;
|
|
191
|
+
this.scheduleClose();
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
private handleFocusIn = (event: FocusEvent): void => {
|
|
196
|
+
const target = event.target as HTMLElement;
|
|
197
|
+
const trigger = target.closest('[slot="trigger"]');
|
|
198
|
+
|
|
199
|
+
if (trigger && this.contains(trigger)) {
|
|
200
|
+
this.clearTimers();
|
|
201
|
+
this.show();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
private handleFocusOut = (event: FocusEvent): void => {
|
|
206
|
+
const relatedTarget = event.relatedTarget as HTMLElement | null;
|
|
207
|
+
|
|
208
|
+
// Check if focus is moving within the hover card
|
|
209
|
+
if (relatedTarget && this.contains(relatedTarget)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Focus left the hover card entirely
|
|
214
|
+
this.scheduleClose();
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
private scheduleOpen(): void {
|
|
218
|
+
this.cancelClose();
|
|
219
|
+
|
|
220
|
+
if (this.open) return;
|
|
221
|
+
|
|
222
|
+
this.openTimer = setTimeout(() => {
|
|
223
|
+
this.show();
|
|
224
|
+
}, this.openDelay);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private scheduleClose(): void {
|
|
228
|
+
this.cancelOpen();
|
|
229
|
+
|
|
230
|
+
// Don't close if still hovering trigger or content
|
|
231
|
+
if (this.isHoveringTrigger || this.isHoveringContent) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!this.open) return;
|
|
236
|
+
|
|
237
|
+
this.closeTimer = setTimeout(() => {
|
|
238
|
+
this.close();
|
|
239
|
+
}, this.closeDelay);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private cancelOpen(): void {
|
|
243
|
+
if (this.openTimer) {
|
|
244
|
+
clearTimeout(this.openTimer);
|
|
245
|
+
this.openTimer = null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private cancelClose(): void {
|
|
250
|
+
if (this.closeTimer) {
|
|
251
|
+
clearTimeout(this.closeTimer);
|
|
252
|
+
this.closeTimer = null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private handleDismiss = (): void => {
|
|
257
|
+
this.close();
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
private setupTriggerAccessibility(): void {
|
|
261
|
+
const trigger = this.querySelector('[slot="trigger"]');
|
|
262
|
+
const content = this.querySelector("ds-hover-card-content");
|
|
263
|
+
|
|
264
|
+
if (trigger && content) {
|
|
265
|
+
trigger.setAttribute("aria-haspopup", "true");
|
|
266
|
+
trigger.setAttribute("aria-expanded", String(this.open));
|
|
267
|
+
if (content.id) {
|
|
268
|
+
trigger.setAttribute("aria-controls", content.id);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private updateTriggerAria(): void {
|
|
274
|
+
const trigger = this.querySelector('[slot="trigger"]');
|
|
275
|
+
if (trigger) {
|
|
276
|
+
trigger.setAttribute("aria-expanded", String(this.open));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private setupPositioning(): void {
|
|
281
|
+
const trigger = this.querySelector('[slot="trigger"]') as HTMLElement | null;
|
|
282
|
+
const content = this.querySelector("ds-hover-card-content") as HTMLElement | null;
|
|
283
|
+
|
|
284
|
+
if (!trigger || !content) return;
|
|
285
|
+
|
|
286
|
+
this.anchorPosition = createAnchorPosition({
|
|
287
|
+
anchor: trigger,
|
|
288
|
+
floating: content,
|
|
289
|
+
placement: this.placement,
|
|
290
|
+
offset: this.offset,
|
|
291
|
+
flip: this.flip,
|
|
292
|
+
onPositionChange: (pos) => {
|
|
293
|
+
content.setAttribute("data-placement", pos.placement);
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
298
|
+
this.anchorPosition?.update();
|
|
299
|
+
});
|
|
300
|
+
this.resizeObserver.observe(trigger);
|
|
301
|
+
this.resizeObserver.observe(content);
|
|
302
|
+
|
|
303
|
+
this.scrollHandler = () => {
|
|
304
|
+
this.anchorPosition?.update();
|
|
305
|
+
};
|
|
306
|
+
window.addEventListener("scroll", this.scrollHandler, { passive: true });
|
|
307
|
+
window.addEventListener("resize", this.scrollHandler, { passive: true });
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private setupDismissLayer(): void {
|
|
311
|
+
const content = this.querySelector("ds-hover-card-content") as HTMLElement | null;
|
|
312
|
+
const trigger = this.querySelector('[slot="trigger"]') as HTMLElement | null;
|
|
313
|
+
|
|
314
|
+
if (!content) return;
|
|
315
|
+
|
|
316
|
+
this.dismissLayer = createDismissableLayer({
|
|
317
|
+
container: content,
|
|
318
|
+
excludeElements: trigger ? [trigger] : [],
|
|
319
|
+
onDismiss: this.handleDismiss,
|
|
320
|
+
closeOnEscape: true,
|
|
321
|
+
closeOnOutsideClick: false, // Non-modal, only close on escape
|
|
322
|
+
});
|
|
323
|
+
this.dismissLayer.activate();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private cleanup(): void {
|
|
327
|
+
this.anchorPosition?.destroy();
|
|
328
|
+
this.anchorPosition = null;
|
|
329
|
+
this.dismissLayer?.deactivate();
|
|
330
|
+
this.dismissLayer = null;
|
|
331
|
+
this.presence?.destroy();
|
|
332
|
+
this.presence = null;
|
|
333
|
+
this.resizeObserver?.disconnect();
|
|
334
|
+
this.resizeObserver = null;
|
|
335
|
+
|
|
336
|
+
if (this.scrollHandler) {
|
|
337
|
+
window.removeEventListener("scroll", this.scrollHandler);
|
|
338
|
+
window.removeEventListener("resize", this.scrollHandler);
|
|
339
|
+
this.scrollHandler = null;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
override async updated(changedProperties: Map<string, unknown>): Promise<void> {
|
|
344
|
+
super.updated(changedProperties);
|
|
345
|
+
|
|
346
|
+
if (changedProperties.has("open")) {
|
|
347
|
+
this.updateTriggerAria();
|
|
348
|
+
|
|
349
|
+
const content = this.querySelector("ds-hover-card-content") as DsHoverCardContent | null;
|
|
350
|
+
|
|
351
|
+
if (this.open) {
|
|
352
|
+
content?.removeAttribute("hidden");
|
|
353
|
+
|
|
354
|
+
if (content) {
|
|
355
|
+
content.dataState = "open";
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
await this.updateComplete;
|
|
359
|
+
|
|
360
|
+
this.setupPositioning();
|
|
361
|
+
this.setupDismissLayer();
|
|
362
|
+
} else {
|
|
363
|
+
content?.setAttribute("hidden", "");
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
override render() {
|
|
369
|
+
return html`
|
|
370
|
+
<slot name="trigger"></slot>
|
|
371
|
+
<slot></slot>
|
|
372
|
+
`;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
define("ds-hover-card", DsHoverCard);
|
|
377
|
+
|
|
378
|
+
declare global {
|
|
379
|
+
interface HTMLElementTagNameMap {
|
|
380
|
+
"ds-hover-card": DsHoverCard;
|
|
381
|
+
}
|
|
382
|
+
}
|