@mkbabb/glass-ui 0.2.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.
Files changed (335) hide show
  1. package/README.md +172 -0
  2. package/dist/glass-ui.css +1 -0
  3. package/dist/glass-ui.js +10019 -0
  4. package/dist/index.d.ts +6619 -0
  5. package/package.json +65 -0
  6. package/src/components/custom/aurora/Aurora.vue +34 -0
  7. package/src/components/custom/aurora/composables/color.ts +122 -0
  8. package/src/components/custom/aurora/composables/useAurora.ts +355 -0
  9. package/src/components/custom/aurora/index.ts +8 -0
  10. package/src/components/custom/confirm-dialog/ConfirmDialog.vue +88 -0
  11. package/src/components/custom/confirm-dialog/index.ts +1 -0
  12. package/src/components/custom/controls/DarkModeToggle.vue +96 -0
  13. package/src/components/custom/controls/index.ts +1 -0
  14. package/src/components/custom/dock/DockLayerGroup.vue +21 -0
  15. package/src/components/custom/dock/DockPopover.vue +263 -0
  16. package/src/components/custom/dock/GlassDock.vue +276 -0
  17. package/src/components/custom/dock/composables/index.ts +16 -0
  18. package/src/components/custom/dock/composables/isTeleportedTarget.ts +19 -0
  19. package/src/components/custom/dock/composables/useDockActionBar.ts +33 -0
  20. package/src/components/custom/dock/composables/useDockState.ts +301 -0
  21. package/src/components/custom/dock/composables/useDockTransition.ts +146 -0
  22. package/src/components/custom/dock/composables/useLayerTransition.ts +135 -0
  23. package/src/components/custom/dock/composables/usePopupMutex.ts +83 -0
  24. package/src/components/custom/dock/index.ts +9 -0
  25. package/src/components/custom/expandable-container/ExpandableContainer.vue +64 -0
  26. package/src/components/custom/expandable-container/index.ts +1 -0
  27. package/src/components/custom/glass-panel/GlassPanel.vue +98 -0
  28. package/src/components/custom/glass-panel/index.ts +2 -0
  29. package/src/components/custom/icon-tooltip/IconTooltip.vue +20 -0
  30. package/src/components/custom/icon-tooltip/index.ts +1 -0
  31. package/src/components/custom/index.ts +15 -0
  32. package/src/components/custom/infinite-scroll/InfiniteScroll.vue +55 -0
  33. package/src/components/custom/infinite-scroll/composables/index.ts +2 -0
  34. package/src/components/custom/infinite-scroll/composables/types.ts +23 -0
  35. package/src/components/custom/infinite-scroll/composables/useInfiniteScroll.ts +73 -0
  36. package/src/components/custom/infinite-scroll/index.ts +1 -0
  37. package/src/components/custom/labeled-field/LabeledInput.vue +29 -0
  38. package/src/components/custom/labeled-field/LabeledSelect.vue +59 -0
  39. package/src/components/custom/labeled-field/LabeledSlider.vue +32 -0
  40. package/src/components/custom/labeled-field/LabeledSwitch.vue +27 -0
  41. package/src/components/custom/labeled-field/index.ts +4 -0
  42. package/src/components/custom/metaballs/MetaballCanvas.vue +23 -0
  43. package/src/components/custom/metaballs/index.ts +4 -0
  44. package/src/components/custom/metaballs/shaders.ts +63 -0
  45. package/src/components/custom/metaballs/types.ts +29 -0
  46. package/src/components/custom/metaballs/useMetaballs.ts +252 -0
  47. package/src/components/custom/search/FuzzySearch.vue +589 -0
  48. package/src/components/custom/search/SearchBar.vue +44 -0
  49. package/src/components/custom/search/composables/fuzzySearchIndex.ts +224 -0
  50. package/src/components/custom/search/composables/index.ts +5 -0
  51. package/src/components/custom/search/composables/types.ts +34 -0
  52. package/src/components/custom/search/composables/useFuzzySearch.ts +115 -0
  53. package/src/components/custom/search/index.ts +7 -0
  54. package/src/components/custom/sidebar/ProgressiveSidebar.vue +256 -0
  55. package/src/components/custom/sidebar/composables/index.ts +6 -0
  56. package/src/components/custom/sidebar/composables/useScrollTracker.ts +242 -0
  57. package/src/components/custom/sidebar/composables/useSidebarFollow.ts +247 -0
  58. package/src/components/custom/sidebar/composables/useSidebarState.ts +72 -0
  59. package/src/components/custom/sidebar/composables/useTreeIndex.ts +152 -0
  60. package/src/components/custom/sidebar/index.ts +15 -0
  61. package/src/components/custom/sidebar/types.ts +50 -0
  62. package/src/components/custom/tabs/BouncyTabs.vue +39 -0
  63. package/src/components/custom/tabs/BouncyToggle.vue +352 -0
  64. package/src/components/custom/tabs/UnderlineTabs.vue +115 -0
  65. package/src/components/custom/tabs/index.ts +5 -0
  66. package/src/components/custom/timeline/GlassTimeline.vue +174 -0
  67. package/src/components/custom/timeline/index.ts +1 -0
  68. package/src/components/custom/typewriter/TypewriterText.vue +239 -0
  69. package/src/components/custom/typewriter/composables/index.ts +1 -0
  70. package/src/components/custom/typewriter/composables/useTypewriter.ts +413 -0
  71. package/src/components/custom/typewriter/index.ts +7 -0
  72. package/src/components/custom/typewriter/types.ts +159 -0
  73. package/src/components/custom/typewriter/utils/keyboard.ts +213 -0
  74. package/src/components/custom/typewriter/utils/pausePatterns.ts +55 -0
  75. package/src/components/custom/typewriter/utils/timing.ts +104 -0
  76. package/src/components/custom/typewriter/utils/typoStateMachine.ts +197 -0
  77. package/src/components/index.ts +2 -0
  78. package/src/components/ui/accordion/Accordion.vue +19 -0
  79. package/src/components/ui/accordion/AccordionContent.vue +24 -0
  80. package/src/components/ui/accordion/AccordionItem.vue +24 -0
  81. package/src/components/ui/accordion/AccordionTrigger.vue +39 -0
  82. package/src/components/ui/accordion/index.ts +4 -0
  83. package/src/components/ui/alert/Alert.vue +20 -0
  84. package/src/components/ui/alert/AlertDescription.vue +17 -0
  85. package/src/components/ui/alert/AlertTitle.vue +17 -0
  86. package/src/components/ui/alert/index.ts +23 -0
  87. package/src/components/ui/avatar/Avatar.vue +21 -0
  88. package/src/components/ui/avatar/AvatarFallback.vue +11 -0
  89. package/src/components/ui/avatar/AvatarImage.vue +9 -0
  90. package/src/components/ui/avatar/index.ts +24 -0
  91. package/src/components/ui/badge/Badge.vue +16 -0
  92. package/src/components/ui/badge/index.ts +25 -0
  93. package/src/components/ui/button/Button.vue +26 -0
  94. package/src/components/ui/button/index.ts +43 -0
  95. package/src/components/ui/card/Card.vue +28 -0
  96. package/src/components/ui/card/CardContent.vue +14 -0
  97. package/src/components/ui/card/CardDescription.vue +14 -0
  98. package/src/components/ui/card/CardFooter.vue +14 -0
  99. package/src/components/ui/card/CardHeader.vue +14 -0
  100. package/src/components/ui/card/CardTitle.vue +21 -0
  101. package/src/components/ui/card/index.ts +6 -0
  102. package/src/components/ui/carousel/Carousel.vue +53 -0
  103. package/src/components/ui/carousel/CarouselContent.vue +35 -0
  104. package/src/components/ui/carousel/CarouselItem.vue +24 -0
  105. package/src/components/ui/carousel/CarouselNext.vue +40 -0
  106. package/src/components/ui/carousel/CarouselPrevious.vue +40 -0
  107. package/src/components/ui/carousel/index.ts +10 -0
  108. package/src/components/ui/carousel/interface.ts +26 -0
  109. package/src/components/ui/carousel/useCarousel.ts +56 -0
  110. package/src/components/ui/checkbox/Checkbox.vue +33 -0
  111. package/src/components/ui/checkbox/index.ts +1 -0
  112. package/src/components/ui/collapsible/Collapsible.vue +15 -0
  113. package/src/components/ui/collapsible/CollapsibleContent.vue +11 -0
  114. package/src/components/ui/collapsible/CollapsibleTrigger.vue +11 -0
  115. package/src/components/ui/collapsible/index.ts +3 -0
  116. package/src/components/ui/combobox/Combobox.vue +17 -0
  117. package/src/components/ui/combobox/ComboboxAnchor.vue +23 -0
  118. package/src/components/ui/combobox/ComboboxEmpty.vue +21 -0
  119. package/src/components/ui/combobox/ComboboxGroup.vue +27 -0
  120. package/src/components/ui/combobox/ComboboxInput.vue +41 -0
  121. package/src/components/ui/combobox/ComboboxItem.vue +24 -0
  122. package/src/components/ui/combobox/ComboboxItemIndicator.vue +23 -0
  123. package/src/components/ui/combobox/ComboboxList.vue +29 -0
  124. package/src/components/ui/combobox/ComboboxSeparator.vue +21 -0
  125. package/src/components/ui/combobox/ComboboxViewport.vue +23 -0
  126. package/src/components/ui/combobox/index.ts +12 -0
  127. package/src/components/ui/command/Command.vue +30 -0
  128. package/src/components/ui/command/CommandDialog.vue +21 -0
  129. package/src/components/ui/command/CommandEmpty.vue +20 -0
  130. package/src/components/ui/command/CommandGroup.vue +29 -0
  131. package/src/components/ui/command/CommandInput.vue +33 -0
  132. package/src/components/ui/command/CommandItem.vue +26 -0
  133. package/src/components/ui/command/CommandList.vue +27 -0
  134. package/src/components/ui/command/CommandSeparator.vue +23 -0
  135. package/src/components/ui/command/CommandShortcut.vue +14 -0
  136. package/src/components/ui/command/index.ts +9 -0
  137. package/src/components/ui/context-menu/ContextMenu.vue +15 -0
  138. package/src/components/ui/context-menu/ContextMenuCheckboxItem.vue +40 -0
  139. package/src/components/ui/context-menu/ContextMenuContent.vue +36 -0
  140. package/src/components/ui/context-menu/ContextMenuGroup.vue +11 -0
  141. package/src/components/ui/context-menu/ContextMenuItem.vue +34 -0
  142. package/src/components/ui/context-menu/ContextMenuLabel.vue +25 -0
  143. package/src/components/ui/context-menu/ContextMenuPortal.vue +11 -0
  144. package/src/components/ui/context-menu/ContextMenuRadioGroup.vue +19 -0
  145. package/src/components/ui/context-menu/ContextMenuRadioItem.vue +40 -0
  146. package/src/components/ui/context-menu/ContextMenuSeparator.vue +20 -0
  147. package/src/components/ui/context-menu/ContextMenuShortcut.vue +14 -0
  148. package/src/components/ui/context-menu/ContextMenuSub.vue +19 -0
  149. package/src/components/ui/context-menu/ContextMenuSubContent.vue +35 -0
  150. package/src/components/ui/context-menu/ContextMenuSubTrigger.vue +34 -0
  151. package/src/components/ui/context-menu/ContextMenuTrigger.vue +13 -0
  152. package/src/components/ui/context-menu/index.ts +14 -0
  153. package/src/components/ui/data-table/DataTable.vue +167 -0
  154. package/src/components/ui/data-table/DataTablePagination.vue +112 -0
  155. package/src/components/ui/data-table/index.ts +3 -0
  156. package/src/components/ui/data-table/types.ts +48 -0
  157. package/src/components/ui/dialog/Dialog.vue +14 -0
  158. package/src/components/ui/dialog/DialogClose.vue +11 -0
  159. package/src/components/ui/dialog/DialogContent.vue +61 -0
  160. package/src/components/ui/dialog/DialogDescription.vue +24 -0
  161. package/src/components/ui/dialog/DialogFooter.vue +19 -0
  162. package/src/components/ui/dialog/DialogHeader.vue +16 -0
  163. package/src/components/ui/dialog/DialogScrollContent.vue +65 -0
  164. package/src/components/ui/dialog/DialogTitle.vue +29 -0
  165. package/src/components/ui/dialog/DialogTrigger.vue +11 -0
  166. package/src/components/ui/dialog/index.ts +9 -0
  167. package/src/components/ui/drawer/Drawer.vue +19 -0
  168. package/src/components/ui/drawer/DrawerContent.vue +28 -0
  169. package/src/components/ui/drawer/DrawerDescription.vue +20 -0
  170. package/src/components/ui/drawer/DrawerFooter.vue +14 -0
  171. package/src/components/ui/drawer/DrawerHeader.vue +14 -0
  172. package/src/components/ui/drawer/DrawerOverlay.vue +18 -0
  173. package/src/components/ui/drawer/DrawerTitle.vue +20 -0
  174. package/src/components/ui/drawer/index.ts +8 -0
  175. package/src/components/ui/dropdown-menu/DropdownMenu.vue +14 -0
  176. package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +40 -0
  177. package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +44 -0
  178. package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +11 -0
  179. package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +28 -0
  180. package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +24 -0
  181. package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +19 -0
  182. package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
  183. package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +22 -0
  184. package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +14 -0
  185. package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
  186. package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +36 -0
  187. package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +33 -0
  188. package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +13 -0
  189. package/src/components/ui/dropdown-menu/index.ts +16 -0
  190. package/src/components/ui/hover-card/HoverCard.vue +14 -0
  191. package/src/components/ui/hover-card/HoverCardContent.vue +41 -0
  192. package/src/components/ui/hover-card/HoverCardTrigger.vue +11 -0
  193. package/src/components/ui/hover-card/index.ts +3 -0
  194. package/src/components/ui/index.ts +41 -0
  195. package/src/components/ui/input/Input.vue +24 -0
  196. package/src/components/ui/input/index.ts +1 -0
  197. package/src/components/ui/label/Label.vue +27 -0
  198. package/src/components/ui/label/index.ts +1 -0
  199. package/src/components/ui/multi-select/MultiSelect.vue +141 -0
  200. package/src/components/ui/multi-select/index.ts +7 -0
  201. package/src/components/ui/notification/Notification.vue +85 -0
  202. package/src/components/ui/notification/index.ts +1 -0
  203. package/src/components/ui/number-field/NumberField.vue +23 -0
  204. package/src/components/ui/number-field/NumberFieldContent.vue +14 -0
  205. package/src/components/ui/number-field/NumberFieldDecrement.vue +25 -0
  206. package/src/components/ui/number-field/NumberFieldIncrement.vue +25 -0
  207. package/src/components/ui/number-field/NumberFieldInput.vue +8 -0
  208. package/src/components/ui/number-field/index.ts +5 -0
  209. package/src/components/ui/popover/Popover.vue +15 -0
  210. package/src/components/ui/popover/PopoverContent.vue +61 -0
  211. package/src/components/ui/popover/PopoverTrigger.vue +11 -0
  212. package/src/components/ui/popover/index.ts +3 -0
  213. package/src/components/ui/progress/Progress.vue +39 -0
  214. package/src/components/ui/progress/index.ts +1 -0
  215. package/src/components/ui/radio-group/RadioGroup.vue +25 -0
  216. package/src/components/ui/radio-group/RadioGroupItem.vue +39 -0
  217. package/src/components/ui/radio-group/index.ts +2 -0
  218. package/src/components/ui/scroll-area/ScrollArea.vue +29 -0
  219. package/src/components/ui/scroll-area/ScrollBar.vue +30 -0
  220. package/src/components/ui/scroll-area/index.ts +2 -0
  221. package/src/components/ui/scroll-pane/ScrollPane.vue +25 -0
  222. package/src/components/ui/scroll-pane/ScrollPaneHeader.vue +75 -0
  223. package/src/components/ui/scroll-pane/index.ts +2 -0
  224. package/src/components/ui/select/Select.vue +15 -0
  225. package/src/components/ui/select/SelectContent.vue +57 -0
  226. package/src/components/ui/select/SelectGroup.vue +19 -0
  227. package/src/components/ui/select/SelectItem.vue +47 -0
  228. package/src/components/ui/select/SelectItemText.vue +11 -0
  229. package/src/components/ui/select/SelectLabel.vue +13 -0
  230. package/src/components/ui/select/SelectScrollDownButton.vue +24 -0
  231. package/src/components/ui/select/SelectScrollUpButton.vue +24 -0
  232. package/src/components/ui/select/SelectSeparator.vue +17 -0
  233. package/src/components/ui/select/SelectTrigger.vue +45 -0
  234. package/src/components/ui/select/SelectValue.vue +11 -0
  235. package/src/components/ui/select/index.ts +11 -0
  236. package/src/components/ui/separator/Separator.vue +35 -0
  237. package/src/components/ui/separator/index.ts +1 -0
  238. package/src/components/ui/sheet/Sheet.vue +14 -0
  239. package/src/components/ui/sheet/SheetClose.vue +11 -0
  240. package/src/components/ui/sheet/SheetContent.vue +56 -0
  241. package/src/components/ui/sheet/SheetDescription.vue +22 -0
  242. package/src/components/ui/sheet/SheetFooter.vue +19 -0
  243. package/src/components/ui/sheet/SheetHeader.vue +16 -0
  244. package/src/components/ui/sheet/SheetTitle.vue +22 -0
  245. package/src/components/ui/sheet/SheetTrigger.vue +11 -0
  246. package/src/components/ui/sheet/index.ts +31 -0
  247. package/src/components/ui/skeleton/Skeleton.vue +14 -0
  248. package/src/components/ui/skeleton/index.ts +1 -0
  249. package/src/components/ui/slider/Slider.vue +66 -0
  250. package/src/components/ui/slider/index.ts +1 -0
  251. package/src/components/ui/switch/Switch.vue +37 -0
  252. package/src/components/ui/switch/index.ts +1 -0
  253. package/src/components/ui/table/Table.vue +16 -0
  254. package/src/components/ui/table/TableBody.vue +14 -0
  255. package/src/components/ui/table/TableCaption.vue +14 -0
  256. package/src/components/ui/table/TableCell.vue +14 -0
  257. package/src/components/ui/table/TableEmpty.vue +39 -0
  258. package/src/components/ui/table/TableFooter.vue +16 -0
  259. package/src/components/ui/table/TableHead.vue +21 -0
  260. package/src/components/ui/table/TableHeader.vue +14 -0
  261. package/src/components/ui/table/TableRow.vue +21 -0
  262. package/src/components/ui/table/index.ts +9 -0
  263. package/src/components/ui/tabs/Tabs.vue +15 -0
  264. package/src/components/ui/tabs/TabsContent.vue +22 -0
  265. package/src/components/ui/tabs/TabsIndicator.vue +22 -0
  266. package/src/components/ui/tabs/TabsList.vue +25 -0
  267. package/src/components/ui/tabs/TabsTrigger.vue +29 -0
  268. package/src/components/ui/tabs/index.ts +5 -0
  269. package/src/components/ui/tags-input/TagsInput.vue +22 -0
  270. package/src/components/ui/tags-input/TagsInputInput.vue +19 -0
  271. package/src/components/ui/tags-input/TagsInputItem.vue +22 -0
  272. package/src/components/ui/tags-input/TagsInputItemDelete.vue +24 -0
  273. package/src/components/ui/tags-input/TagsInputItemText.vue +19 -0
  274. package/src/components/ui/tags-input/index.ts +5 -0
  275. package/src/components/ui/textarea/Textarea.vue +24 -0
  276. package/src/components/ui/textarea/index.ts +1 -0
  277. package/src/components/ui/toast/Toast.vue +57 -0
  278. package/src/components/ui/toast/ToastAction.vue +30 -0
  279. package/src/components/ui/toast/ToastClose.vue +31 -0
  280. package/src/components/ui/toast/ToastDescription.vue +25 -0
  281. package/src/components/ui/toast/ToastTitle.vue +25 -0
  282. package/src/components/ui/toast/Toaster.vue +31 -0
  283. package/src/components/ui/toast/index.ts +8 -0
  284. package/src/components/ui/toast/use-toast.ts +136 -0
  285. package/src/components/ui/toggle/Toggle.vue +35 -0
  286. package/src/components/ui/toggle/index.ts +27 -0
  287. package/src/components/ui/toggle-group/ToggleGroup.vue +34 -0
  288. package/src/components/ui/toggle-group/ToggleGroupItem.vue +35 -0
  289. package/src/components/ui/toggle-group/index.ts +2 -0
  290. package/src/components/ui/tooltip/Tooltip.vue +14 -0
  291. package/src/components/ui/tooltip/TooltipContent.vue +31 -0
  292. package/src/components/ui/tooltip/TooltipProvider.vue +11 -0
  293. package/src/components/ui/tooltip/TooltipTrigger.vue +11 -0
  294. package/src/components/ui/tooltip/index.ts +4 -0
  295. package/src/composables/glass/index.ts +8 -0
  296. package/src/composables/glass/useGlassRenderer.ts +252 -0
  297. package/src/composables/glass/webgl/frostShader.ts +221 -0
  298. package/src/composables/glass/webgpu/glassShader.wgsl +173 -0
  299. package/src/composables/index.ts +32 -0
  300. package/src/composables/infinite-scroll/index.ts +2 -0
  301. package/src/composables/infinite-scroll/types.ts +25 -0
  302. package/src/composables/infinite-scroll/useInfiniteScroll.ts +101 -0
  303. package/src/composables/interaction/index.ts +5 -0
  304. package/src/composables/interaction/useHeightTransition.ts +82 -0
  305. package/src/composables/interaction/useHoverPopover.ts +64 -0
  306. package/src/composables/interaction/useHoverToggle.ts +103 -0
  307. package/src/composables/interaction/useLeaveTimer.ts +17 -0
  308. package/src/composables/interaction/useTouchGate.ts +207 -0
  309. package/src/composables/pagination/index.ts +2 -0
  310. package/src/composables/pagination/useOffsetPagination.ts +70 -0
  311. package/src/composables/prng.ts +32 -0
  312. package/src/composables/useCharSplit.ts +31 -0
  313. package/src/composables/useClipboard.ts +46 -0
  314. package/src/composables/useGlobalDark.ts +61 -0
  315. package/src/composables/useKeyboardShortcuts.ts +205 -0
  316. package/src/composables/useWatercolorBlob.ts +136 -0
  317. package/src/composables/virtual/index.ts +22 -0
  318. package/src/composables/virtual/useVirtualSectionWindow.ts +338 -0
  319. package/src/composables/virtual/useWindowedStore.ts +86 -0
  320. package/src/composables/virtual/virtualSectionLayout.ts +212 -0
  321. package/src/index.ts +9 -0
  322. package/src/styles/animations.css +233 -0
  323. package/src/styles/cards.css +66 -0
  324. package/src/styles/dock.css +221 -0
  325. package/src/styles/floating-panel.css +49 -0
  326. package/src/styles/glass.css +266 -0
  327. package/src/styles/index.css +26 -0
  328. package/src/styles/scroll-pane.css +10 -0
  329. package/src/styles/theme.css +138 -0
  330. package/src/styles/tokens.css +333 -0
  331. package/src/styles/transitions.css +226 -0
  332. package/src/styles/typography.css +277 -0
  333. package/src/styles/utilities.css +697 -0
  334. package/src/utils/cn.ts +6 -0
  335. package/src/utils/index.ts +1 -0
@@ -0,0 +1,96 @@
1
+ <template>
2
+ <!-- Credit to Kevin Powell at https://codepen.io/kevinpowell/pen/PomqjxO -->
3
+ <component
4
+ :is="passive ? 'div' : 'button'"
5
+ class="dark-mode-toggle-button"
6
+ v-bind="$attrs"
7
+ @click="!passive && toggleDark()"
8
+ >
9
+ <svg
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ width="472.39"
12
+ height="472.39"
13
+ viewBox="0 0 472.39 472.39"
14
+ >
15
+ <g class="toggle-sun">
16
+ <path
17
+ d="M403.21,167V69.18H305.38L236.2,0,167,69.18H69.18V167L0,236.2l69.18,69.18v97.83H167l69.18,69.18,69.18-69.18h97.83V305.38l69.18-69.18Zm-167,198.17a129,129,0,1,1,129-129A129,129,0,0,1,236.2,365.19Z"
18
+ />
19
+ </g>
20
+ <g class="toggle-circle">
21
+ <circle cx="236.2" cy="236.2" r="90" />
22
+ </g>
23
+ </svg>
24
+ </component>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { watchEffect } from "vue";
29
+ import { useGlobalDark } from "../../../composables/useGlobalDark";
30
+
31
+ const props = defineProps<{
32
+ passive?: boolean;
33
+ /**
34
+ * When true, CSS transitions on `<html>` and all descendants are
35
+ * temporarily suppressed during dark mode toggle to prevent jank.
36
+ * @default false
37
+ */
38
+ disableTransitions?: boolean;
39
+ }>();
40
+
41
+ const { toggleDark, setDisableTransitions } = useGlobalDark();
42
+
43
+ watchEffect(() => {
44
+ setDisableTransitions(props.disableTransitions ?? false);
45
+ });
46
+ </script>
47
+
48
+ <style scoped>
49
+ .dark-mode-toggle-button {
50
+ cursor: pointer;
51
+ border: 0;
52
+ padding: 0;
53
+ border-radius: var(--radius-pill);
54
+ position: relative;
55
+ isolation: isolate;
56
+ background: none;
57
+ opacity: 0.8;
58
+
59
+ transition: opacity var(--duration-normal) var(--ease-standard),
60
+ background var(--duration-normal) var(--ease-standard);
61
+
62
+ z-index: var(--z-popover);
63
+
64
+ svg {
65
+ fill: var(--foreground);
66
+ width: 100%;
67
+ height: 100%;
68
+ }
69
+
70
+ &:hover,
71
+ &:focus {
72
+ outline: none;
73
+ opacity: 1;
74
+ background: hsl(0 0% 50% / 0.15);
75
+ }
76
+ }
77
+
78
+ .toggle-sun {
79
+ transform-origin: center center;
80
+ transition: transform 750ms var(--spring-bouncy);
81
+ }
82
+
83
+ .toggle-circle {
84
+ transform: translateX(0%);
85
+ transition: transform 500ms var(--ease-out);
86
+ }
87
+
88
+ /* Dark mode styles — use :where(.dark) so it doesn't leak to <html> */
89
+ :where(.dark) .dark-mode-toggle-button .toggle-sun {
90
+ transform: rotate(0.5turn);
91
+ }
92
+
93
+ :where(.dark) .dark-mode-toggle-button .toggle-circle {
94
+ transform: translateX(-15%);
95
+ }
96
+ </style>
@@ -0,0 +1 @@
1
+ export { default as DarkModeToggle } from "./DarkModeToggle.vue";
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import { toRef, useTemplateRef } from "vue";
3
+ import { useLayerTransition } from "./composables/useLayerTransition";
4
+
5
+ const props = defineProps<{
6
+ activeLayer: string;
7
+ }>();
8
+
9
+ const containerEl = useTemplateRef<HTMLElement>("containerEl");
10
+
11
+ const { layerProps, onTransitionEnd } = useLayerTransition({
12
+ containerEl,
13
+ activeLayer: toRef(props, "activeLayer"),
14
+ });
15
+ </script>
16
+
17
+ <template>
18
+ <div ref="containerEl" class="dock-layer-grid" @transitionend="onTransitionEnd">
19
+ <slot :layer-props="layerProps" />
20
+ </div>
21
+ </template>
@@ -0,0 +1,263 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Compact popover that expands from a dock button.
4
+ * Stays open while mouse is inside. Click to toggle.
5
+ * Auto-flips direction and adjusts horizontal position to avoid clipping.
6
+ */
7
+ import { ref, inject, watch, onUnmounted, nextTick, useTemplateRef, type CSSProperties } from "vue";
8
+ import type { Ref } from "vue";
9
+
10
+ // Track all popover instances so opening one collapses the others
11
+ const allPopovers = new Set<{ expanded: { value: boolean }, scheduleCollapse: (d: number) => void }>();
12
+ let popoverZCounter = 0;
13
+
14
+ const props = withDefaults(
15
+ defineProps<{
16
+ direction?: "up" | "down";
17
+ collapseDelay?: number;
18
+ align?: "center" | "end";
19
+ clickOnly?: boolean;
20
+ }>(),
21
+ { direction: "down", collapseDelay: 1200, align: "center", clickOnly: false },
22
+ );
23
+
24
+ const expanded = ref(false);
25
+ const zOffset = ref(0);
26
+ const panelStyle = ref<CSSProperties>({});
27
+ const resolvedDir = ref(props.direction);
28
+ let collapseTimer: ReturnType<typeof setTimeout> | null = null;
29
+
30
+ const self = { expanded, scheduleCollapse: (d: number) => scheduleCollapse(d) };
31
+ allPopovers.add(self);
32
+ onUnmounted(() => { clearTimer(); removeClickAwayListener(); allPopovers.delete(self); });
33
+
34
+ // Hold parent GlassDock open while this popover is expanded
35
+ const dockKeepOpen = inject<(() => void) | null>("dockKeepOpen", null);
36
+ const dockRelease = inject<(() => void) | null>("dockRelease", null);
37
+
38
+ // Guard: inject parent dock's expanded state
39
+ const dockExpanded = inject<Ref<boolean>>("dockExpanded", ref(true));
40
+
41
+ watch(expanded, (isExpanded) => {
42
+ if (isExpanded) dockKeepOpen?.();
43
+ else dockRelease?.();
44
+ });
45
+
46
+ // Force-close popover when parent dock collapses
47
+ watch(dockExpanded, (isExpanded) => {
48
+ if (!isExpanded && expanded.value) {
49
+ expanded.value = false;
50
+ }
51
+ });
52
+
53
+ function clearTimer() {
54
+ if (collapseTimer) { clearTimeout(collapseTimer); collapseTimer = null; }
55
+ }
56
+
57
+ function onEnter() {
58
+ // Guard: don't open if parent dock is collapsing
59
+ if (!dockExpanded.value) return;
60
+ clearTimer();
61
+ // Collapse all other popovers
62
+ for (const p of allPopovers) {
63
+ if (p !== self && p.expanded.value) p.scheduleCollapse(0);
64
+ }
65
+ zOffset.value = ++popoverZCounter;
66
+ expanded.value = true;
67
+ }
68
+
69
+ function scheduleCollapse(delay: number) {
70
+ clearTimer();
71
+ collapseTimer = setTimeout(() => { expanded.value = false; }, delay);
72
+ }
73
+
74
+ function toggle() {
75
+ expanded.value ? scheduleCollapse(0) : onEnter();
76
+ }
77
+
78
+ // --- Position the panel after it mounts to avoid clipping ---
79
+ const popoverEl = useTemplateRef<HTMLElement>("popoverEl");
80
+ const panelEl = useTemplateRef<HTMLElement>("panelEl");
81
+ const OFFSET = 6;
82
+ const VIEWPORT_PAD = 8;
83
+
84
+ function positionPanel() {
85
+ const trigger = popoverEl.value;
86
+ const panel = panelEl.value;
87
+ if (!trigger || !panel) return;
88
+
89
+ const triggerRect = trigger.getBoundingClientRect();
90
+ const panelRect = panel.getBoundingClientRect();
91
+ const vw = window.innerWidth;
92
+ const vh = window.innerHeight;
93
+
94
+ // Decide direction: prefer props.direction, flip if clipping
95
+ let dir = props.direction;
96
+ if (dir === "up" && triggerRect.top - panelRect.height - OFFSET < VIEWPORT_PAD) {
97
+ dir = "down";
98
+ } else if (dir === "down" && triggerRect.bottom + panelRect.height + OFFSET > vh - VIEWPORT_PAD) {
99
+ dir = "up";
100
+ }
101
+ resolvedDir.value = dir;
102
+
103
+ const style: CSSProperties = {
104
+ position: "absolute",
105
+ zIndex: 50 + (zOffset.value % 50),
106
+ };
107
+
108
+ // Vertical
109
+ if (dir === "up") {
110
+ style.bottom = `calc(100% + ${OFFSET}px)`;
111
+ style.top = "auto";
112
+ } else {
113
+ style.top = `calc(100% + ${OFFSET}px)`;
114
+ style.bottom = "auto";
115
+ }
116
+
117
+ // Horizontal
118
+ if (props.align === "end") {
119
+ style.right = "0";
120
+ style.left = "auto";
121
+ style.transform = "none";
122
+ // Check right-edge clipping (panel extends left from trigger's right edge)
123
+ const panelLeft = triggerRect.right - panelRect.width;
124
+ if (panelLeft < VIEWPORT_PAD) {
125
+ // Shift right so panel starts at viewport edge
126
+ style.right = "auto";
127
+ style.left = `${VIEWPORT_PAD - triggerRect.left}px`;
128
+ }
129
+ } else {
130
+ // center alignment
131
+ style.left = "50%";
132
+ style.right = "auto";
133
+ style.transform = "translateX(-50%)";
134
+
135
+ // Check horizontal clipping
136
+ const panelCenterLeft = triggerRect.left + triggerRect.width / 2 - panelRect.width / 2;
137
+ const panelCenterRight = panelCenterLeft + panelRect.width;
138
+
139
+ if (panelCenterLeft < VIEWPORT_PAD) {
140
+ const shift = VIEWPORT_PAD - panelCenterLeft;
141
+ style.transform = `translateX(calc(-50% + ${shift}px))`;
142
+ } else if (panelCenterRight > vw - VIEWPORT_PAD) {
143
+ const shift = panelCenterRight - (vw - VIEWPORT_PAD);
144
+ style.transform = `translateX(calc(-50% - ${shift}px))`;
145
+ }
146
+ }
147
+
148
+ panelStyle.value = style;
149
+ }
150
+
151
+ watch(expanded, async (isExpanded) => {
152
+ if (isExpanded) {
153
+ installClickAway();
154
+ // Position after the panel mounts
155
+ await nextTick();
156
+ positionPanel();
157
+ } else {
158
+ removeClickAwayListener();
159
+ }
160
+ });
161
+
162
+ // --- Click-away: collapse when clicking outside .dock-popover ---
163
+ let removeClickAwayFn: (() => void) | null = null;
164
+
165
+ function onClickAway(e: PointerEvent) {
166
+ const root = popoverEl.value;
167
+ if (!root || root.contains(e.target as Node)) return;
168
+ expanded.value = false;
169
+ }
170
+
171
+ function installClickAway() {
172
+ nextTick(() => {
173
+ document.addEventListener("pointerdown", onClickAway, true);
174
+ removeClickAwayFn = () => {
175
+ document.removeEventListener("pointerdown", onClickAway, true);
176
+ removeClickAwayFn = null;
177
+ };
178
+ });
179
+ }
180
+
181
+ function removeClickAwayListener() {
182
+ removeClickAwayFn?.();
183
+ }
184
+
185
+ defineExpose({ expanded, expand: onEnter, collapse: () => { expanded.value = false; } });
186
+ </script>
187
+
188
+ <template>
189
+ <div
190
+ ref="popoverEl"
191
+ class="dock-popover"
192
+ :class="{ expanded, ['dir-' + resolvedDir]: true, ['align-' + align]: true }"
193
+ @mouseenter="!clickOnly && onEnter()"
194
+ @mouseleave="!clickOnly && scheduleCollapse(collapseDelay)"
195
+ >
196
+ <button class="popover-trigger dock-icon-btn" @click.stop="toggle">
197
+ <slot name="trigger" />
198
+ </button>
199
+ <Transition :name="'pop-' + resolvedDir">
200
+ <div v-if="expanded" ref="panelEl" class="popover-panel" :style="panelStyle"
201
+ @click.stop @mousedown.stop @pointerdown.stop>
202
+ <slot />
203
+ </div>
204
+ </Transition>
205
+ </div>
206
+ </template>
207
+
208
+ <style scoped>
209
+ .dock-popover {
210
+ position: relative;
211
+ display: flex;
212
+ align-items: center;
213
+ flex-shrink: 0;
214
+ }
215
+ .popover-trigger {
216
+ z-index: 2;
217
+ position: relative;
218
+ }
219
+
220
+ .popover-panel {
221
+ position: absolute;
222
+ display: flex;
223
+ flex-direction: column;
224
+ align-items: stretch;
225
+ pointer-events: auto;
226
+ overflow: hidden;
227
+ gap: 0.125rem;
228
+ padding: 0.25rem;
229
+ z-index: var(--z-modal);
230
+ background: var(--glass-bg-elevated);
231
+ backdrop-filter: var(--glass-blur-elevated);
232
+ -webkit-backdrop-filter: var(--glass-blur-elevated);
233
+ border: 1px solid var(--glass-border-elevated);
234
+ border-radius: var(--radius-panel);
235
+ box-shadow: var(--glass-shadow-elevated);
236
+ }
237
+
238
+ /* ── Spring transitions ── */
239
+ .pop-up-enter-active,
240
+ .pop-down-enter-active {
241
+ transition: opacity var(--duration-fast) var(--ease-standard), transform var(--duration-slow) var(--spring-snappy);
242
+ }
243
+ .pop-up-leave-active,
244
+ .pop-down-leave-active {
245
+ transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-out);
246
+ }
247
+ .pop-up-enter-from {
248
+ opacity: 0;
249
+ transform: scale(0.85) translateY(8px);
250
+ }
251
+ .pop-up-leave-to {
252
+ opacity: 0;
253
+ transform: scale(0.95) translateY(4px);
254
+ }
255
+ .pop-down-enter-from {
256
+ opacity: 0;
257
+ transform: scale(0.85) translateY(-8px);
258
+ }
259
+ .pop-down-leave-to {
260
+ opacity: 0;
261
+ transform: scale(0.95) translateY(-4px);
262
+ }
263
+ </style>
@@ -0,0 +1,276 @@
1
+ <script setup lang="ts">
2
+ import { computed, onMounted, useTemplateRef } from "vue";
3
+ import { useDockState } from "./composables/useDockState";
4
+ import { useDockTransition } from "./composables/useDockTransition";
5
+
6
+ const props = withDefaults(
7
+ defineProps<{
8
+ collapseDelay?: number;
9
+ startCollapsed?: boolean;
10
+ fitContent?: boolean;
11
+ position?: "fixed" | "inline" | "sticky";
12
+ fadeMs?: number;
13
+ alwaysExpanded?: boolean;
14
+ /** Allow expanded content to wrap to multiple lines. */
15
+ wrap?: boolean;
16
+ }>(),
17
+ {
18
+ collapseDelay: 2000,
19
+ startCollapsed: true,
20
+ fitContent: false,
21
+ position: "inline",
22
+ fadeMs: 60,
23
+ alwaysExpanded: false,
24
+ wrap: false,
25
+ },
26
+ );
27
+
28
+ const dockEl = useTemplateRef<HTMLElement>("dockEl");
29
+ const alwaysExpanded = computed(() => props.alwaysExpanded);
30
+
31
+ const {
32
+ expanded,
33
+ isPinned,
34
+ onMouseEnter,
35
+ onMouseLeave,
36
+ onFocusIn,
37
+ onFocusOut,
38
+ onClickCollapsed,
39
+ keepOpen,
40
+ release,
41
+ expand,
42
+ collapse,
43
+ } = useDockState({
44
+ collapseDelay: props.collapseDelay,
45
+ rootEl: dockEl,
46
+ alwaysExpanded,
47
+ });
48
+
49
+ const { visualExpanded, isTransitioning, transitionWidth, suppressTransition, onTransitionEnd } = useDockTransition({
50
+ expanded,
51
+ rootEl: dockEl,
52
+ fadeMs: props.fadeMs,
53
+ alwaysExpanded,
54
+ });
55
+
56
+ const dockStyle = computed(() =>
57
+ transitionWidth.value != null ? { width: transitionWidth.value } : undefined
58
+ );
59
+
60
+ onMounted(() => {
61
+ if (props.alwaysExpanded || !props.startCollapsed) {
62
+ expand();
63
+ }
64
+ });
65
+
66
+ defineExpose({ expanded, isPinned, expand, collapse, keepOpen, release });
67
+ </script>
68
+
69
+ <template>
70
+ <div
71
+ ref="dockEl"
72
+ class="glass-dock"
73
+ :class="[
74
+ { expanded: visualExpanded, collapsed: !visualExpanded, pinned: isPinned, 'fit-content': fitContent, 'always-expanded': alwaysExpanded, 'dock-wrap': wrap, 'no-transition': suppressTransition },
75
+ position === 'fixed' ? 'fixed bottom-[var(--dock-pos)] left-1/2 -translate-x-1/2'
76
+ : position === 'sticky' ? 'dock-sticky'
77
+ : 'dock-inline',
78
+ ]"
79
+ :style="dockStyle"
80
+ @mouseenter="onMouseEnter"
81
+ @mouseleave="onMouseLeave($event)"
82
+ @focusin="onFocusIn"
83
+ @focusout="onFocusOut"
84
+ @transitionend="onTransitionEnd"
85
+ >
86
+ <div class="dock-layers" :class="{ 'dock-transitioning': isTransitioning }">
87
+ <div
88
+ :class="['dock-layer dock-layer--full', { 'layer-active': visualExpanded }]"
89
+ :inert="!expanded || undefined"
90
+ >
91
+ <slot />
92
+ </div>
93
+ <div
94
+ :class="['dock-layer dock-layer--summary', { 'layer-active': !visualExpanded }]"
95
+ :inert="expanded || undefined"
96
+ @click="onClickCollapsed"
97
+ >
98
+ <slot name="collapsed" />
99
+ </div>
100
+ </div>
101
+ </div>
102
+ </template>
103
+
104
+ <style scoped>
105
+ .glass-dock {
106
+ display: inline-flex;
107
+ align-items: center;
108
+ border-radius: var(--radius-dock);
109
+ white-space: nowrap;
110
+ overflow: hidden;
111
+ padding: 0.375rem 0.5rem;
112
+ background: var(--glass-bg-medium);
113
+ backdrop-filter: var(--glass-blur-subtle);
114
+ -webkit-backdrop-filter: var(--glass-blur-subtle);
115
+ border: 1.5px solid var(--glass-border-medium);
116
+ box-shadow: var(--shadow-dock);
117
+ transition:
118
+ width var(--duration-normal) var(--spring-snappy),
119
+ padding var(--duration-normal) var(--spring-snappy),
120
+ box-shadow var(--duration-normal) var(--ease-standard),
121
+ transform var(--duration-normal) var(--spring-snappy),
122
+ background var(--duration-normal) var(--ease-standard),
123
+ border-color var(--duration-normal) var(--ease-standard);
124
+ }
125
+
126
+ /* ── Collapsed: compact pill (round when icon-only) ── */
127
+ .glass-dock.collapsed {
128
+ cursor: pointer;
129
+ padding: 0.375rem;
130
+ justify-content: center;
131
+ background: var(--glass-bg-subtle);
132
+ border-color: var(--glass-border-elevated);
133
+ box-shadow: var(--shadow-dock-collapsed);
134
+ }
135
+
136
+ /* When collapsed content is just an icon (no text), force square = circle.
137
+ The dock-layer height is 2.5rem; with 0.375rem padding each side that's
138
+ the intrinsic height. We match width to height via the same constraint. */
139
+ .glass-dock.collapsed .dock-layer--summary {
140
+ min-width: 2.5rem;
141
+ justify-content: center;
142
+ }
143
+
144
+ .glass-dock.collapsed:hover {
145
+ background: var(--glass-bg-subtle);
146
+ border-color: var(--glass-border-elevated);
147
+ box-shadow: var(--shadow-dock);
148
+ transform: scale(1.03);
149
+ }
150
+
151
+ .glass-dock:where(.fixed) {
152
+ z-index: var(--z-dock);
153
+ }
154
+
155
+ .dock-inline {
156
+ margin: 0 auto;
157
+ }
158
+
159
+ .dock-sticky {
160
+ position: sticky;
161
+ top: 0;
162
+ z-index: var(--z-dock);
163
+ margin: 0 auto;
164
+ }
165
+
166
+ /* ── Layer stacking via grid ── */
167
+ .dock-layers {
168
+ display: grid;
169
+ transition: opacity var(--duration-instant) var(--ease-standard);
170
+ }
171
+
172
+ .dock-layers.dock-transitioning {
173
+ opacity: 0;
174
+ pointer-events: none;
175
+ }
176
+
177
+ .dock-layer {
178
+ display: flex;
179
+ align-items: center;
180
+ white-space: nowrap;
181
+ grid-area: 1 / 1;
182
+ gap: 0.375rem;
183
+ height: 2.5rem;
184
+ }
185
+
186
+ .dock-layer.layer-active {
187
+ pointer-events: auto;
188
+ }
189
+
190
+ .dock-layer:not(.layer-active) {
191
+ pointer-events: none;
192
+ position: absolute;
193
+ visibility: hidden;
194
+ }
195
+
196
+ .glass-dock.expanded {
197
+ overflow: visible;
198
+ }
199
+
200
+ /* When the dock stretches to a set width (not fit-content), layers fill it */
201
+ .glass-dock.expanded:not(.fit-content) .dock-layers {
202
+ width: 100%;
203
+ }
204
+
205
+ .glass-dock.expanded:not(.fit-content) .dock-layer--full {
206
+ width: 100%;
207
+ }
208
+
209
+ .glass-dock.no-transition {
210
+ transition: none !important;
211
+ }
212
+
213
+ .glass-dock.always-expanded {
214
+ cursor: default;
215
+ overflow: visible;
216
+ }
217
+
218
+ /* ── Wrap variant: multi-line responsive dock ──
219
+ Mobile: content wraps, dock grows both axes, rounded-rect.
220
+ Desktop (sm+): single-row pill, same as default. */
221
+ .glass-dock.dock-wrap {
222
+ white-space: normal;
223
+ border-radius: var(--radius-2xl);
224
+ max-width: calc(100vw - 1rem);
225
+ padding: 0.375rem 0.625rem;
226
+ }
227
+
228
+ .glass-dock.dock-wrap .dock-layer--full {
229
+ flex-wrap: wrap;
230
+ justify-content: center;
231
+ height: auto;
232
+ min-height: 2rem;
233
+ gap: 0.25rem 0.375rem;
234
+ }
235
+
236
+ .glass-dock.dock-wrap .dock-layer--summary {
237
+ height: auto;
238
+ min-height: 2rem;
239
+ }
240
+
241
+ /* Hide vertical separators when content wraps — they don't make sense between rows */
242
+ .glass-dock.dock-wrap :deep(.dock-separator) {
243
+ display: none;
244
+ }
245
+
246
+ /* Collapsed stays pill */
247
+ .glass-dock.dock-wrap.collapsed {
248
+ border-radius: var(--radius-pill);
249
+ white-space: nowrap;
250
+ max-width: none;
251
+ }
252
+
253
+ /* Desktop: revert to single-row pill */
254
+ @media (min-width: 640px) {
255
+ .glass-dock.dock-wrap {
256
+ white-space: nowrap;
257
+ border-radius: var(--radius-pill);
258
+ max-width: none;
259
+ padding: 0.375rem 0.75rem;
260
+ }
261
+
262
+ .glass-dock.dock-wrap .dock-layer--full {
263
+ flex-wrap: nowrap;
264
+ height: 2.5rem;
265
+ gap: 0.25rem;
266
+ }
267
+
268
+ .glass-dock.dock-wrap .dock-layer--summary {
269
+ height: 2.5rem;
270
+ }
271
+
272
+ .glass-dock.dock-wrap :deep(.dock-separator) {
273
+ display: block;
274
+ }
275
+ }
276
+ </style>
@@ -0,0 +1,16 @@
1
+ export { useDockState } from "./useDockState";
2
+ export type { UseDockStateOptions, DockState } from "./useDockState";
3
+
4
+ export { useDockTransition } from "./useDockTransition";
5
+ export type { UseDockTransitionOptions } from "./useDockTransition";
6
+
7
+ export { useLayerTransition } from "./useLayerTransition";
8
+ export type { UseLayerTransitionOptions, UseLayerTransitionReturn } from "./useLayerTransition";
9
+
10
+ export { usePopupMutex } from "./usePopupMutex";
11
+ export type { UsePopupMutexOptions, UsePopupMutexReturn } from "./usePopupMutex";
12
+
13
+ export { DOCK_ACTION_BAR_KEY } from "./useDockActionBar";
14
+ export type { DockAction, DockActionBar } from "./useDockActionBar";
15
+
16
+ export { isTeleportedTarget } from "./isTeleportedTarget";
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Detect whether a DOM target is inside a reka-ui teleported overlay
3
+ * (dropdown, select, popover, menu) or a glass-ui floating panel.
4
+ *
5
+ * Used by useDockState and DockPopover to distinguish "logically inside
6
+ * the dock" clicks from true outside clicks. Uses Element (not HTMLElement)
7
+ * so SVG icon targets inside dropdowns are caught too.
8
+ */
9
+ export function isTeleportedTarget(target: EventTarget | null): boolean {
10
+ if (!(target instanceof Element)) return false;
11
+ return !!(
12
+ target.closest('[data-reka-popper-content-wrapper]') ||
13
+ target.closest('[data-reka-menu-content]') ||
14
+ target.closest('[role="menu"]') ||
15
+ target.closest('[role="listbox"]') ||
16
+ target.closest('.floating-panel') ||
17
+ target.closest('.dock-popover')
18
+ );
19
+ }