@rimelight/ui 0.0.1

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 (301) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/i18n-data.json +186 -0
  4. package/package.json +49 -0
  5. package/src/components/FormattedDate.astro +22 -0
  6. package/src/components/HeaderLink.astro +24 -0
  7. package/src/components/HttpObservatory.astro +103 -0
  8. package/src/components/LighthouseScores.astro +204 -0
  9. package/src/components/MissingTranslationBanner.astro +39 -0
  10. package/src/components/TableOfContents.astro +106 -0
  11. package/src/components/ThemeToggle.astro +71 -0
  12. package/src/components/accordion/Accordion.astro +257 -0
  13. package/src/components/accordion/AccordionContent.astro +34 -0
  14. package/src/components/accordion/AccordionItem.astro +29 -0
  15. package/src/components/accordion/AccordionTrigger.astro +33 -0
  16. package/src/components/alert/Alert.astro +34 -0
  17. package/src/components/alert/AlertDescription.astro +15 -0
  18. package/src/components/alert/AlertTitle.astro +17 -0
  19. package/src/components/alert-dialog/AlertDialog.astro +275 -0
  20. package/src/components/alert-dialog/AlertDialogAction.astro +46 -0
  21. package/src/components/alert-dialog/AlertDialogCancel.astro +46 -0
  22. package/src/components/alert-dialog/AlertDialogContent.astro +52 -0
  23. package/src/components/alert-dialog/AlertDialogDescription.astro +18 -0
  24. package/src/components/alert-dialog/AlertDialogFooter.astro +16 -0
  25. package/src/components/alert-dialog/AlertDialogHeader.astro +14 -0
  26. package/src/components/alert-dialog/AlertDialogTitle.astro +20 -0
  27. package/src/components/alert-dialog/AlertDialogTrigger.astro +47 -0
  28. package/src/components/aspect-ratio/AspectRatio.astro +32 -0
  29. package/src/components/avatar/Avatar.astro +29 -0
  30. package/src/components/avatar/AvatarFallback.astro +18 -0
  31. package/src/components/avatar/AvatarImage.astro +66 -0
  32. package/src/components/badge/Badge.astro +57 -0
  33. package/src/components/breadcrumb/Breadcrumb.astro +12 -0
  34. package/src/components/breadcrumb/BreadcrumbEllipsis.astro +29 -0
  35. package/src/components/breadcrumb/BreadcrumbItem.astro +15 -0
  36. package/src/components/breadcrumb/BreadcrumbLink.astro +24 -0
  37. package/src/components/breadcrumb/BreadcrumbList.astro +17 -0
  38. package/src/components/breadcrumb/BreadcrumbPage.astro +22 -0
  39. package/src/components/breadcrumb/BreadcrumbSeparator.astro +24 -0
  40. package/src/components/button/Button.astro +58 -0
  41. package/src/components/button-group/ButtonGroup.astro +64 -0
  42. package/src/components/button-group/ButtonGroupSeparator.astro +20 -0
  43. package/src/components/button-group/ButtonGroupText.astro +19 -0
  44. package/src/components/card/Card.astro +36 -0
  45. package/src/components/card/CardAction.astro +16 -0
  46. package/src/components/card/CardContent.astro +16 -0
  47. package/src/components/card/CardDescription.astro +16 -0
  48. package/src/components/card/CardFooter.astro +16 -0
  49. package/src/components/card/CardHeader.astro +19 -0
  50. package/src/components/card/CardTitle.astro +16 -0
  51. package/src/components/carousel/Carousel.astro +55 -0
  52. package/src/components/carousel/CarouselContent.astro +26 -0
  53. package/src/components/carousel/CarouselItem.astro +26 -0
  54. package/src/components/carousel/CarouselNext.astro +39 -0
  55. package/src/components/carousel/CarouselPrevious.astro +41 -0
  56. package/src/components/carousel/carousel-script.ts +188 -0
  57. package/src/components/carousel/index.ts +1 -0
  58. package/src/components/checkbox/Checkbox.astro +133 -0
  59. package/src/components/collapsible/Collapsible.astro +161 -0
  60. package/src/components/collapsible/CollapsibleContent.astro +22 -0
  61. package/src/components/collapsible/CollapsibleTrigger.astro +44 -0
  62. package/src/components/dialog/Dialog.astro +389 -0
  63. package/src/components/dialog/DialogClose.astro +35 -0
  64. package/src/components/dialog/DialogContent.astro +78 -0
  65. package/src/components/dialog/DialogDescription.astro +14 -0
  66. package/src/components/dialog/DialogFooter.astro +14 -0
  67. package/src/components/dialog/DialogHeader.astro +14 -0
  68. package/src/components/dialog/DialogTitle.astro +18 -0
  69. package/src/components/dialog/DialogTrigger.astro +47 -0
  70. package/src/components/dropdown/Dropdown.astro +604 -0
  71. package/src/components/dropdown/DropdownContent.astro +118 -0
  72. package/src/components/dropdown/DropdownItem.astro +48 -0
  73. package/src/components/dropdown/DropdownLabel.astro +29 -0
  74. package/src/components/dropdown/DropdownSeparator.astro +21 -0
  75. package/src/components/dropdown/DropdownShortcut.astro +17 -0
  76. package/src/components/dropdown/DropdownSub.astro +15 -0
  77. package/src/components/dropdown/DropdownSubContent.astro +39 -0
  78. package/src/components/dropdown/DropdownSubTrigger.astro +34 -0
  79. package/src/components/dropdown/DropdownTrigger.astro +52 -0
  80. package/src/components/dropzone/Dropzone.astro +236 -0
  81. package/src/components/dropzone/DropzoneFilesList.astro +26 -0
  82. package/src/components/dropzone/DropzoneLoadingIndicator.astro +10 -0
  83. package/src/components/dropzone/DropzoneUploadIndicator.astro +10 -0
  84. package/src/components/embeds/YoutubeEmbed.astro +94 -0
  85. package/src/components/hero/Hero.astro +166 -0
  86. package/src/components/icons/Flag.astro +62 -0
  87. package/src/components/image/ExtendedImage.astro +46 -0
  88. package/src/components/image/Image.astro +25 -0
  89. package/src/components/input/Input.astro +27 -0
  90. package/src/components/input-group/InputGroup.astro +28 -0
  91. package/src/components/input-group/InputGroupAddon.astro +76 -0
  92. package/src/components/input-group/InputGroupButton.astro +36 -0
  93. package/src/components/input-group/InputGroupInput.astro +18 -0
  94. package/src/components/input-group/InputGroupText.astro +17 -0
  95. package/src/components/input-group/InputGroupTextarea.astro +22 -0
  96. package/src/components/input-otp/InputOtp.astro +352 -0
  97. package/src/components/input-otp/InputOtpGroup.astro +16 -0
  98. package/src/components/input-otp/InputOtpSeparator.astro +25 -0
  99. package/src/components/input-otp/InputOtpSlot.astro +50 -0
  100. package/src/components/input-otp/InputOtpTypes.ts +6 -0
  101. package/src/components/item/Item.astro +52 -0
  102. package/src/components/item/ItemActions.astro +16 -0
  103. package/src/components/item/ItemContent.astro +16 -0
  104. package/src/components/item/ItemDescription.astro +19 -0
  105. package/src/components/item/ItemFooter.astro +16 -0
  106. package/src/components/item/ItemGroup.astro +16 -0
  107. package/src/components/item/ItemHeader.astro +16 -0
  108. package/src/components/item/ItemMedia.astro +40 -0
  109. package/src/components/item/ItemSeparator.astro +23 -0
  110. package/src/components/item/ItemTitle.astro +16 -0
  111. package/src/components/kbd/Kbd.astro +21 -0
  112. package/src/components/kbd/KbdGroup.astro +16 -0
  113. package/src/components/label/Label.astro +24 -0
  114. package/src/components/marquee/Marquee.astro +73 -0
  115. package/src/components/native-select/NativeSelect.astro +68 -0
  116. package/src/components/native-select/NativeSelectOptGroup.astro +15 -0
  117. package/src/components/native-select/NativeSelectOption.astro +15 -0
  118. package/src/components/pagination/Pagination.astro +20 -0
  119. package/src/components/pagination/PaginationContent.astro +16 -0
  120. package/src/components/pagination/PaginationEllipsis.astro +37 -0
  121. package/src/components/pagination/PaginationItem.astro +16 -0
  122. package/src/components/pagination/PaginationLink.astro +27 -0
  123. package/src/components/pagination/PaginationNext.astro +33 -0
  124. package/src/components/pagination/PaginationPrevious.astro +33 -0
  125. package/src/components/popover/Popover.astro +717 -0
  126. package/src/components/popover/PopoverContent.astro +102 -0
  127. package/src/components/popover/PopoverDescription.astro +14 -0
  128. package/src/components/popover/PopoverHeader.astro +14 -0
  129. package/src/components/popover/PopoverTitle.astro +14 -0
  130. package/src/components/popover/PopoverTrigger.astro +51 -0
  131. package/src/components/progress/Progress.astro +154 -0
  132. package/src/components/prose/Prose.astro +619 -0
  133. package/src/components/radio-group/RadioGroup.astro +162 -0
  134. package/src/components/radio-group/RadioGroupItem.astro +133 -0
  135. package/src/components/radio-group/RadioGroupTypes.ts +6 -0
  136. package/src/components/section/Section.astro +167 -0
  137. package/src/components/select/Select.astro +752 -0
  138. package/src/components/select/SelectContent.astro +94 -0
  139. package/src/components/select/SelectGroup.astro +9 -0
  140. package/src/components/select/SelectItem.astro +51 -0
  141. package/src/components/select/SelectLabel.astro +14 -0
  142. package/src/components/select/SelectSearch.astro +49 -0
  143. package/src/components/select/SelectSeparator.astro +12 -0
  144. package/src/components/select/SelectTrigger.astro +56 -0
  145. package/src/components/select/SelectTypes.ts +13 -0
  146. package/src/components/select/SelectValue.astro +19 -0
  147. package/src/components/separator/Separator.astro +36 -0
  148. package/src/components/sheet/Sheet.astro +15 -0
  149. package/src/components/sheet/SheetClose.astro +15 -0
  150. package/src/components/sheet/SheetContent.astro +89 -0
  151. package/src/components/sheet/SheetDescription.astro +16 -0
  152. package/src/components/sheet/SheetFooter.astro +16 -0
  153. package/src/components/sheet/SheetHeader.astro +16 -0
  154. package/src/components/sheet/SheetTitle.astro +16 -0
  155. package/src/components/sheet/SheetTrigger.astro +15 -0
  156. package/src/components/sidebar/Sidebar.astro +231 -0
  157. package/src/components/sidebar/SidebarContent.astro +25 -0
  158. package/src/components/sidebar/SidebarFooter.astro +22 -0
  159. package/src/components/sidebar/SidebarGroup.astro +22 -0
  160. package/src/components/sidebar/SidebarGroupContent.astro +22 -0
  161. package/src/components/sidebar/SidebarGroupLabel.astro +53 -0
  162. package/src/components/sidebar/SidebarHeader.astro +22 -0
  163. package/src/components/sidebar/SidebarInput.astro +24 -0
  164. package/src/components/sidebar/SidebarInset.astro +22 -0
  165. package/src/components/sidebar/SidebarMenu.astro +22 -0
  166. package/src/components/sidebar/SidebarMenuAction.astro +63 -0
  167. package/src/components/sidebar/SidebarMenuBadge.astro +31 -0
  168. package/src/components/sidebar/SidebarMenuButton.astro +124 -0
  169. package/src/components/sidebar/SidebarMenuItem.astro +22 -0
  170. package/src/components/sidebar/SidebarMenuSkeleton.astro +43 -0
  171. package/src/components/sidebar/SidebarMenuSub.astro +25 -0
  172. package/src/components/sidebar/SidebarMenuSubButton.astro +52 -0
  173. package/src/components/sidebar/SidebarMenuSubItem.astro +17 -0
  174. package/src/components/sidebar/SidebarProvider.astro +215 -0
  175. package/src/components/sidebar/SidebarRail.astro +72 -0
  176. package/src/components/sidebar/SidebarSeparator.astro +25 -0
  177. package/src/components/sidebar/SidebarTrigger.astro +69 -0
  178. package/src/components/skeleton/Skeleton.astro +14 -0
  179. package/src/components/slider/Slider.astro +425 -0
  180. package/src/components/spinner/Spinner.astro +23 -0
  181. package/src/components/switch/Switch.astro +192 -0
  182. package/src/components/switch/SwitchTypes.ts +6 -0
  183. package/src/components/table/Table.astro +20 -0
  184. package/src/components/table/TableBody.astro +18 -0
  185. package/src/components/table/TableCaption.astro +18 -0
  186. package/src/components/table/TableCell.astro +18 -0
  187. package/src/components/table/TableFoot.astro +18 -0
  188. package/src/components/table/TableHead.astro +18 -0
  189. package/src/components/table/TableHeader.astro +18 -0
  190. package/src/components/table/TableRow.astro +17 -0
  191. package/src/components/tabs/Tabs.astro +270 -0
  192. package/src/components/tabs/TabsContent.astro +28 -0
  193. package/src/components/tabs/TabsList.astro +23 -0
  194. package/src/components/tabs/TabsTrigger.astro +34 -0
  195. package/src/components/textarea/Textarea.astro +31 -0
  196. package/src/components/toast/ToastDescription.astro +22 -0
  197. package/src/components/toast/ToastItem.astro +56 -0
  198. package/src/components/toast/ToastTemplate.astro +27 -0
  199. package/src/components/toast/ToastTitle.astro +59 -0
  200. package/src/components/toast/Toaster.astro +981 -0
  201. package/src/components/toast/index.ts +1 -0
  202. package/src/components/toast/toast-manager.ts +234 -0
  203. package/src/components/toggle/Toggle.astro +174 -0
  204. package/src/components/toggle/ToggleTypes.ts +14 -0
  205. package/src/components/tooltip/Tooltip.astro +422 -0
  206. package/src/components/tooltip/TooltipContent.astro +83 -0
  207. package/src/components/tooltip/TooltipTrigger.astro +13 -0
  208. package/src/components/video/Video.astro +121 -0
  209. package/src/config/cookies.ts +5 -0
  210. package/src/config/index.ts +3 -0
  211. package/src/config/links.ts +4 -0
  212. package/src/config/site.config.ts +45 -0
  213. package/src/domain/cms/index.ts +1 -0
  214. package/src/domain/cms/types.ts +20 -0
  215. package/src/domain/i18n/constants.ts +21 -0
  216. package/src/domain/i18n/cookie-storage.ts +13 -0
  217. package/src/domain/i18n/country-to-locale-map.ts +67 -0
  218. package/src/domain/i18n/fallbacks.generated.ts +230 -0
  219. package/src/domain/i18n/fetcher.ts +268 -0
  220. package/src/domain/i18n/flags.ts +16 -0
  221. package/src/domain/i18n/format.ts +90 -0
  222. package/src/domain/i18n/index.ts +10 -0
  223. package/src/domain/i18n/middleware/i18n.ts +160 -0
  224. package/src/domain/i18n/resolve-locale.ts +55 -0
  225. package/src/domain/i18n/runtime-constants.ts +14 -0
  226. package/src/domain/i18n/schema.ts +29 -0
  227. package/src/domain/index.ts +4 -0
  228. package/src/domain/seo/constants.ts +20 -0
  229. package/src/domain/seo/index.ts +3 -0
  230. package/src/domain/seo/services/index.ts +3 -0
  231. package/src/domain/seo/services/llms.ts +53 -0
  232. package/src/domain/seo/services/robots.ts +12 -0
  233. package/src/domain/seo/services/sitemap.ts +56 -0
  234. package/src/domain/seo/utils.ts +37 -0
  235. package/src/domain/theme/constants.ts +6 -0
  236. package/src/domain/theme/index.ts +2 -0
  237. package/src/domain/theme/schema.ts +10 -0
  238. package/src/env.d.ts +49 -0
  239. package/src/i18n.generated.d.ts +202 -0
  240. package/src/lib/component-preview.ts +73 -0
  241. package/src/lib/index.ts +6 -0
  242. package/src/lib/remark/remark-modified-time.ts +13 -0
  243. package/src/lib/showcase-preview-elements.ts +408 -0
  244. package/src/lib/showcase-renderer.ts +218 -0
  245. package/src/lib/showcase-utils.ts +189 -0
  246. package/src/lib/turnstile.ts +35 -0
  247. package/src/lib/utils/starwind/positioning.ts +318 -0
  248. package/src/locales/de/blog.json +9 -0
  249. package/src/locales/de/common.json +19 -0
  250. package/src/locales/de/errors.json +6 -0
  251. package/src/locales/de/home.json +20 -0
  252. package/src/locales/de/messages.json +8 -0
  253. package/src/locales/de/nav.json +13 -0
  254. package/src/locales/de/projects.json +9 -0
  255. package/src/locales/de/services.json +43 -0
  256. package/src/locales/en/blog.json +9 -0
  257. package/src/locales/en/chat.json +29 -0
  258. package/src/locales/en/common.json +19 -0
  259. package/src/locales/en/cookie_notice.json +11 -0
  260. package/src/locales/en/editor.json +24 -0
  261. package/src/locales/en/errors.json +6 -0
  262. package/src/locales/en/home.json +20 -0
  263. package/src/locales/en/messages.json +8 -0
  264. package/src/locales/en/nav.json +13 -0
  265. package/src/locales/en/projects.json +9 -0
  266. package/src/locales/en/services.json +43 -0
  267. package/src/locales/es/blog.json +9 -0
  268. package/src/locales/es/common.json +19 -0
  269. package/src/locales/es/errors.json +6 -0
  270. package/src/locales/es/home.json +20 -0
  271. package/src/locales/es/messages.json +8 -0
  272. package/src/locales/es/nav.json +13 -0
  273. package/src/locales/es/projects.json +9 -0
  274. package/src/locales/es/services.json +43 -0
  275. package/src/locales/ja/blog.json +9 -0
  276. package/src/locales/ja/common.json +19 -0
  277. package/src/locales/ja/errors.json +6 -0
  278. package/src/locales/ja/home.json +20 -0
  279. package/src/locales/ja/messages.json +8 -0
  280. package/src/locales/ja/nav.json +13 -0
  281. package/src/locales/ja/projects.json +9 -0
  282. package/src/locales/ja/services.json +43 -0
  283. package/src/locales/pt/blog.json +9 -0
  284. package/src/locales/pt/chat.json +29 -0
  285. package/src/locales/pt/common.json +19 -0
  286. package/src/locales/pt/cookie_notice.json +11 -0
  287. package/src/locales/pt/editor.json +24 -0
  288. package/src/locales/pt/errors.json +6 -0
  289. package/src/locales/pt/home.json +20 -0
  290. package/src/locales/pt/messages.json +8 -0
  291. package/src/locales/pt/nav.json +13 -0
  292. package/src/locales/pt/projects.json +9 -0
  293. package/src/locales/pt/services.json +43 -0
  294. package/src/utils/index.ts +2 -0
  295. package/src/utils/server/cookies.ts +98 -0
  296. package/src/utils/server/index.ts +1 -0
  297. package/src/utils/shared/deep-merge.ts +36 -0
  298. package/src/utils/shared/embed.ts +105 -0
  299. package/src/utils/shared/index.ts +3 -0
  300. package/src/utils/shared/parse-boolean.ts +14 -0
  301. package/worker-configuration.d.ts +14791 -0
@@ -0,0 +1,717 @@
1
+ ---
2
+ import type { HTMLAttributes } from "astro/types";
3
+ import { cv } from "css-variants";
4
+
5
+ type Props = HTMLAttributes<"div"> & {
6
+ /**
7
+ * When true, the popover will open on hover in addition to click
8
+ * @default false
9
+ */
10
+ openOnHover?: boolean;
11
+ /**
12
+ * Time in milliseconds to wait before closing when hover-open is enabled
13
+ * @default 200
14
+ */
15
+ closeDelay?: number;
16
+ /**
17
+ * Whether the popover should be open when first rendered
18
+ * @default false
19
+ */
20
+ defaultOpen?: boolean;
21
+
22
+ children: any;
23
+ };
24
+
25
+ export const popover = cv({ base: "starwind-popover" });
26
+
27
+ const {
28
+ class: className,
29
+ openOnHover = false,
30
+ closeDelay = 200,
31
+ defaultOpen = false,
32
+ ...rest
33
+ } = Astro.props;
34
+ ---
35
+
36
+ <div
37
+ class:list={[popover(), className]}
38
+ data-open-on-hover={openOnHover ? "true" : undefined}
39
+ data-close-delay={closeDelay}
40
+ data-state={defaultOpen ? "open" : "closed"}
41
+ data-default-open={defaultOpen ? "true" : undefined}
42
+ {...rest}
43
+ data-slot="popover"
44
+ >
45
+ <slot />
46
+ </div>
47
+
48
+ <script>
49
+ import {
50
+ type FloatingAlign,
51
+ type FloatingSide,
52
+ getTransformOrigin,
53
+ resolvePlacement,
54
+ } from "../../lib/utils/starwind/positioning";
55
+
56
+ let globalPointerClientX: number | null = null;
57
+ let globalPointerClientY: number | null = null;
58
+ let globalPointerTrackingRefCount = 0;
59
+ let isGlobalPointerTrackingAttached = false;
60
+
61
+ const handleGlobalPointerMove = (e: PointerEvent) => {
62
+ if (e.pointerType !== "mouse") return;
63
+ globalPointerClientX = e.clientX;
64
+ globalPointerClientY = e.clientY;
65
+ };
66
+
67
+ const registerGlobalPointerTracking = () => {
68
+ globalPointerTrackingRefCount += 1;
69
+
70
+ if (isGlobalPointerTrackingAttached) {
71
+ return;
72
+ }
73
+
74
+ document.addEventListener("pointermove", handleGlobalPointerMove);
75
+ isGlobalPointerTrackingAttached = true;
76
+ };
77
+
78
+ const unregisterGlobalPointerTracking = () => {
79
+ globalPointerTrackingRefCount = Math.max(0, globalPointerTrackingRefCount - 1);
80
+
81
+ if (globalPointerTrackingRefCount > 0 || !isGlobalPointerTrackingAttached) {
82
+ return;
83
+ }
84
+
85
+ document.removeEventListener("pointermove", handleGlobalPointerMove);
86
+ isGlobalPointerTrackingAttached = false;
87
+ globalPointerClientX = null;
88
+ globalPointerClientY = null;
89
+ };
90
+
91
+ class PopoverHandler {
92
+ private popover: HTMLElement;
93
+ private trigger: HTMLElement | null;
94
+ private content: HTMLElement | null;
95
+ private title: HTMLElement | null;
96
+ private description: HTMLElement | null;
97
+ private isOpen: boolean = false;
98
+ private isClosing: boolean = false;
99
+ private animationDuration = 150;
100
+ private side: FloatingSide = "bottom";
101
+ private align: FloatingAlign = "center";
102
+ private sideOffset = 4;
103
+ private parentHandler: PopoverHandler | null = null;
104
+ private contentPlaceholder: Comment | null = null;
105
+ private cleanupAutoUpdate: (() => void) | null = null;
106
+ private abortController: AbortController;
107
+ private closeTimeoutRef: number | null = null;
108
+ private openOnHover = false;
109
+ private hoverCloseDelay = 200;
110
+ private hoverCloseTimerRef: number | null = null;
111
+ private openChildCount = 0;
112
+ private registeredWithParentAsOpenChild = false;
113
+ private usesGlobalPointerTracking = false;
114
+
115
+ constructor(popover: HTMLElement, popoverIdx: number) {
116
+ this.popover = popover;
117
+ this.abortController = new AbortController();
118
+ this.trigger = this.findOwnElement('[data-slot="popover-trigger"]');
119
+ this.content = this.findOwnElement('[data-slot="popover-content"]');
120
+ this.title = this.findOwnElement('[data-slot="popover-title"]');
121
+ this.description = this.findOwnElement('[data-slot="popover-description"]');
122
+
123
+ if (this.content) {
124
+ (this.content as any).__popoverHandler = this;
125
+ }
126
+
127
+ this.openOnHover = this.popover.getAttribute("data-open-on-hover") === "true";
128
+ const closeDelay = parseInt(this.popover.getAttribute("data-close-delay") || "200");
129
+ if (Number.isFinite(closeDelay) && closeDelay >= 0) {
130
+ this.hoverCloseDelay = closeDelay;
131
+ }
132
+
133
+ this.parentHandler = this.resolveParentHandler();
134
+
135
+ // if trigger is set with asChild, use the first child element as trigger button
136
+ if (this.trigger?.hasAttribute("data-as-child")) {
137
+ this.trigger = this.trigger.firstElementChild as HTMLElement;
138
+ }
139
+
140
+ if (!this.trigger || !this.content) return;
141
+
142
+ if (this.openOnHover) {
143
+ registerGlobalPointerTracking();
144
+ this.usesGlobalPointerTracking = true;
145
+ }
146
+
147
+ const animationDurationString = this.content.style.animationDuration;
148
+ if (animationDurationString.endsWith("ms")) {
149
+ this.animationDuration = parseFloat(animationDurationString);
150
+ } else if (animationDurationString.endsWith("s")) {
151
+ this.animationDuration = parseFloat(animationDurationString) * 1000;
152
+ }
153
+
154
+ const contentSide = this.content.getAttribute("data-side");
155
+ if (
156
+ contentSide === "top" ||
157
+ contentSide === "bottom" ||
158
+ contentSide === "left" ||
159
+ contentSide === "right"
160
+ ) {
161
+ this.side = contentSide;
162
+ }
163
+
164
+ const contentAlign = this.content.getAttribute("data-align");
165
+ if (contentAlign === "start" || contentAlign === "center" || contentAlign === "end") {
166
+ this.align = contentAlign;
167
+ }
168
+
169
+ const contentSideOffset = parseFloat(this.content.getAttribute("data-side-offset") || "4");
170
+ if (Number.isFinite(contentSideOffset)) {
171
+ this.sideOffset = contentSideOffset;
172
+ }
173
+
174
+ this.isOpen =
175
+ this.popover.getAttribute("data-state") === "open" ||
176
+ this.popover.getAttribute("data-default-open") === "true";
177
+
178
+ this.init(popoverIdx);
179
+ }
180
+
181
+ private findOwnElement(selector: string): HTMLElement | null {
182
+ const elements = this.popover.querySelectorAll(selector);
183
+ for (const element of elements) {
184
+ if (element.closest(".starwind-popover") === this.popover) {
185
+ return element as HTMLElement;
186
+ }
187
+ }
188
+ return null;
189
+ }
190
+
191
+ private resolveParentHandler() {
192
+ const parentContent = this.popover.parentElement?.closest('[data-slot="popover-content"]');
193
+ if (!parentContent) return null;
194
+
195
+ return ((parentContent as any).__popoverHandler as PopoverHandler | undefined) ?? null;
196
+ }
197
+
198
+ private init(popoverIdx: number) {
199
+ this.setupAccessibility(popoverIdx);
200
+ this.setupEvents();
201
+ this.setInitialState();
202
+ }
203
+
204
+ private setupAccessibility(popoverIdx: number) {
205
+ if (!this.trigger || !this.content) return;
206
+
207
+ if (!this.trigger.id) {
208
+ this.trigger.id = `starwind-popover${popoverIdx}-trigger`;
209
+ }
210
+
211
+ if (!this.content.id) {
212
+ this.content.id = `starwind-popover${popoverIdx}-content`;
213
+ }
214
+
215
+ this.trigger.setAttribute("aria-controls", this.content.id);
216
+ this.trigger.setAttribute("aria-haspopup", "dialog");
217
+ this.content.setAttribute("aria-labelledby", this.trigger.id);
218
+
219
+ if (this.title) {
220
+ if (!this.title.id) {
221
+ this.title.id = `starwind-popover${popoverIdx}-title`;
222
+ }
223
+ this.content.setAttribute("aria-labelledby", this.title.id);
224
+ }
225
+
226
+ if (this.description) {
227
+ if (!this.description.id) {
228
+ this.description.id = `starwind-popover${popoverIdx}-description`;
229
+ }
230
+ this.content.setAttribute("aria-describedby", this.description.id);
231
+ }
232
+ }
233
+
234
+ private setupEvents() {
235
+ if (!this.trigger || !this.content) return;
236
+
237
+ this.trigger.addEventListener("click", (e) => {
238
+ if (this.isTriggerDisabled()) return;
239
+ e.preventDefault();
240
+ this.togglePopover();
241
+ });
242
+
243
+ this.trigger.addEventListener("keydown", (e) => {
244
+ if (this.isTriggerDisabled()) return;
245
+
246
+ if (e.key === "Enter" || e.key === " ") {
247
+ e.preventDefault();
248
+ e.stopPropagation();
249
+ this.togglePopover();
250
+ } else if (e.key === "ArrowDown" && !this.isOpen) {
251
+ e.preventDefault();
252
+ e.stopPropagation();
253
+ this.openPopover();
254
+ } else if (e.key === "Escape" && this.isOpen) {
255
+ e.preventDefault();
256
+ e.stopPropagation();
257
+ this.closePopover({ focusTrigger: true });
258
+ }
259
+ });
260
+
261
+ this.content.addEventListener("keydown", (e) => {
262
+ if (e.key === "Escape" && this.isOpen) {
263
+ e.preventDefault();
264
+ e.stopPropagation();
265
+ this.closePopover({ focusTrigger: true });
266
+ }
267
+ });
268
+
269
+ if (this.openOnHover) {
270
+ this.trigger.addEventListener(
271
+ "pointerenter",
272
+ (e) => {
273
+ if (e.pointerType !== "mouse") return;
274
+ if (this.isTriggerDisabled()) return;
275
+
276
+ if (!this.isOpen) {
277
+ this.openPopover();
278
+ } else {
279
+ this.clearHoverCloseTimersInTree();
280
+ }
281
+ },
282
+ { signal: this.abortController.signal },
283
+ );
284
+
285
+ this.trigger.addEventListener(
286
+ "pointerleave",
287
+ (e) => {
288
+ if (e.pointerType !== "mouse") return;
289
+ if (this.isOpen) {
290
+ this.closePopoverDelayed();
291
+ }
292
+ },
293
+ { signal: this.abortController.signal },
294
+ );
295
+
296
+ this.content.addEventListener(
297
+ "pointerenter",
298
+ (e) => {
299
+ if (e.pointerType !== "mouse") return;
300
+ this.clearHoverCloseTimersInTree();
301
+ },
302
+ { signal: this.abortController.signal },
303
+ );
304
+
305
+ this.content.addEventListener(
306
+ "pointerleave",
307
+ (e) => {
308
+ if (e.pointerType !== "mouse") return;
309
+ if (this.isOpen) {
310
+ this.closePopoverDelayed();
311
+ }
312
+ },
313
+ { signal: this.abortController.signal },
314
+ );
315
+ }
316
+
317
+ this.popover.addEventListener("starwind-popover:close", ((
318
+ event: CustomEvent<{ focusTrigger?: boolean }>,
319
+ ) => {
320
+ this.closePopover(event.detail?.focusTrigger !== undefined ? { focusTrigger: event.detail.focusTrigger } : {});
321
+ }) as EventListener);
322
+
323
+ document.addEventListener(
324
+ "pointerdown",
325
+ (e) => {
326
+ if (
327
+ this.isOpen &&
328
+ !this.popover.contains(e.target as Node) &&
329
+ !this.content?.contains(e.target as Node) &&
330
+ !this.isTargetInPopoverTree(e.target as Node)
331
+ ) {
332
+ this.closePopover();
333
+ }
334
+ },
335
+ { signal: this.abortController.signal },
336
+ );
337
+
338
+ document.addEventListener(
339
+ "click",
340
+ (e) => {
341
+ if (
342
+ this.isOpen &&
343
+ !this.popover.contains(e.target as Node) &&
344
+ !this.content?.contains(e.target as Node) &&
345
+ !this.isTargetInPopoverTree(e.target as Node)
346
+ ) {
347
+ this.closePopover();
348
+ }
349
+ },
350
+ { signal: this.abortController.signal },
351
+ );
352
+ }
353
+
354
+ private setInitialState() {
355
+ if (!this.trigger || !this.content) return;
356
+
357
+ if (this.isOpen) {
358
+ this.popover.setAttribute("data-state", "open");
359
+ this.trigger.setAttribute("aria-expanded", "true");
360
+ this.trigger.setAttribute("data-state", "open");
361
+ this.content.setAttribute("data-state", "open");
362
+ this.content.style.removeProperty("display");
363
+ this.portalContent();
364
+ this.positionContent();
365
+ requestAnimationFrame(() => this.positionContent());
366
+ } else {
367
+ this.popover.setAttribute("data-state", "closed");
368
+ this.trigger.setAttribute("aria-expanded", "false");
369
+ this.trigger.setAttribute("data-state", "closed");
370
+ this.content.setAttribute("data-state", "closed");
371
+ this.unportalContent();
372
+ this.content.style.display = "none";
373
+ }
374
+ }
375
+
376
+ private isTriggerDisabled() {
377
+ if (!this.trigger) return true;
378
+ return (
379
+ this.trigger.hasAttribute("disabled") ||
380
+ this.trigger.getAttribute("aria-disabled") === "true" ||
381
+ this.trigger.getAttribute("data-disabled") === "true"
382
+ );
383
+ }
384
+
385
+ private togglePopover() {
386
+ if (this.isOpen) {
387
+ this.closePopover();
388
+ } else {
389
+ this.openPopover();
390
+ }
391
+ }
392
+
393
+ private openPopover() {
394
+ if (this.isOpen || this.isClosing) return;
395
+ if (!this.trigger || !this.content || this.isTriggerDisabled()) return;
396
+
397
+ this.clearHoverCloseTimersInTree();
398
+ this.registerAsOpenChildOnParent();
399
+
400
+ this.isOpen = true;
401
+ this.popover.setAttribute("data-state", "open");
402
+ this.trigger.setAttribute("aria-expanded", "true");
403
+ this.trigger.setAttribute("data-state", "open");
404
+ this.content.setAttribute("data-state", "open");
405
+ this.content.style.removeProperty("display");
406
+ this.portalContent();
407
+ this.positionContent();
408
+ requestAnimationFrame(() => this.positionContent());
409
+ }
410
+
411
+ private closePopover(options?: { focusTrigger?: boolean }) {
412
+ if (!this.isOpen) return;
413
+ if (!this.trigger || !this.content) return;
414
+
415
+ this.clearHoverCloseTimer();
416
+
417
+ this.closeNestedPopovers();
418
+
419
+ this.isClosing = true;
420
+ this.isOpen = false;
421
+ this.popover.setAttribute("data-state", "closed");
422
+ this.trigger.setAttribute("aria-expanded", "false");
423
+ this.trigger.setAttribute("data-state", "closed");
424
+ this.content.setAttribute("data-state", "closed");
425
+
426
+ if (options?.focusTrigger) {
427
+ requestAnimationFrame(() => {
428
+ this.trigger?.focus();
429
+ });
430
+ }
431
+
432
+ if (this.closeTimeoutRef !== null) {
433
+ window.clearTimeout(this.closeTimeoutRef);
434
+ }
435
+
436
+ this.closeTimeoutRef = window.setTimeout(() => {
437
+ this.closeTimeoutRef = null;
438
+ if (this.abortController.signal.aborted) return;
439
+ if (!this.content) return;
440
+ this.unportalContent();
441
+ this.content.style.display = "none";
442
+ this.unregisterAsOpenChildOnParent();
443
+ this.isClosing = false;
444
+ }, this.animationDuration);
445
+ }
446
+
447
+ private closeNestedPopovers() {
448
+ if (!this.content) return;
449
+
450
+ const nestedPopovers = this.content.querySelectorAll<HTMLElement>(
451
+ '.starwind-popover[data-state="open"]',
452
+ );
453
+
454
+ for (const nestedPopover of nestedPopovers) {
455
+ if (nestedPopover !== this.popover) {
456
+ nestedPopover.dispatchEvent(
457
+ new CustomEvent("starwind-popover:close", { detail: { focusTrigger: false } }),
458
+ );
459
+ }
460
+ }
461
+ }
462
+
463
+ destroy() {
464
+ if (this.abortController.signal.aborted) return;
465
+
466
+ this.abortController.abort();
467
+
468
+ if (this.usesGlobalPointerTracking) {
469
+ unregisterGlobalPointerTracking();
470
+ this.usesGlobalPointerTracking = false;
471
+ }
472
+
473
+ this.clearHoverCloseTimer();
474
+
475
+ if (this.closeTimeoutRef !== null) {
476
+ window.clearTimeout(this.closeTimeoutRef);
477
+ this.closeTimeoutRef = null;
478
+ }
479
+
480
+ this.unportalContent();
481
+ this.unregisterAsOpenChildOnParent();
482
+ this.isOpen = false;
483
+ this.isClosing = false;
484
+
485
+ if (this.content) {
486
+ delete (this.content as any).__popoverHandler;
487
+ }
488
+ }
489
+
490
+ private closePopoverDelayed() {
491
+ if (!this.openOnHover) return;
492
+
493
+ this.clearHoverCloseTimer();
494
+
495
+ this.hoverCloseTimerRef = window.setTimeout(() => {
496
+ this.hoverCloseTimerRef = null;
497
+ if (this.abortController.signal.aborted) return;
498
+ if (this.openChildCount > 0) return;
499
+ if (this.isFocusWithinPopoverTree()) return;
500
+ if (this.isPointerWithinPopoverTree()) return;
501
+ if (this.isOpen) {
502
+ this.closePopover();
503
+ }
504
+ }, this.hoverCloseDelay);
505
+ }
506
+
507
+ private clearHoverCloseTimer() {
508
+ if (this.hoverCloseTimerRef !== null) {
509
+ window.clearTimeout(this.hoverCloseTimerRef);
510
+ this.hoverCloseTimerRef = null;
511
+ }
512
+ }
513
+
514
+ private clearHoverCloseTimersInTree() {
515
+ this.clearHoverCloseTimer();
516
+
517
+ let currentHandler = this.parentHandler;
518
+ while (currentHandler) {
519
+ currentHandler.clearHoverCloseTimer();
520
+ currentHandler = currentHandler.parentHandler;
521
+ }
522
+ }
523
+
524
+ private isFocusWithinPopoverTree(): boolean {
525
+ const activeElement = document.activeElement;
526
+ if (!(activeElement instanceof Node)) return false;
527
+
528
+ return (
529
+ this.popover.contains(activeElement) ||
530
+ this.content?.contains(activeElement) ||
531
+ this.isTargetInPopoverTree(activeElement)
532
+ );
533
+ }
534
+
535
+ private isPointerWithinPopoverTree(): boolean {
536
+ if (this.trigger?.matches(":hover") || this.content?.matches(":hover")) {
537
+ return true;
538
+ }
539
+
540
+ if (globalPointerClientX === null || globalPointerClientY === null) {
541
+ return false;
542
+ }
543
+
544
+ const pointerTarget = document.elementFromPoint(globalPointerClientX, globalPointerClientY);
545
+ if (!(pointerTarget instanceof Node)) {
546
+ return false;
547
+ }
548
+
549
+ return (
550
+ this.popover.contains(pointerTarget) ||
551
+ this.content?.contains(pointerTarget) ||
552
+ this.isTargetInPopoverTree(pointerTarget)
553
+ );
554
+ }
555
+
556
+ private registerAsOpenChildOnParent() {
557
+ if (!this.parentHandler || this.registeredWithParentAsOpenChild) return;
558
+
559
+ this.parentHandler.openChildCount++;
560
+ this.registeredWithParentAsOpenChild = true;
561
+ }
562
+
563
+ private unregisterAsOpenChildOnParent() {
564
+ if (!this.parentHandler || !this.registeredWithParentAsOpenChild) return;
565
+
566
+ this.parentHandler.openChildCount = Math.max(0, this.parentHandler.openChildCount - 1);
567
+ this.registeredWithParentAsOpenChild = false;
568
+ this.parentHandler.handleChildPopoverClosed();
569
+ }
570
+
571
+ private handleChildPopoverClosed() {
572
+ if (!this.openOnHover || !this.isOpen) return;
573
+ if (this.openChildCount > 0) return;
574
+ if (this.isFocusWithinPopoverTree()) return;
575
+ if (this.isPointerWithinPopoverTree()) return;
576
+
577
+ this.closePopoverDelayed();
578
+ }
579
+
580
+ private isTargetInPopoverTree(target: Node): boolean {
581
+ const element = target instanceof HTMLElement ? target : target.parentElement;
582
+ if (!element) return false;
583
+
584
+ const contentElement = element.closest('[data-slot="popover-content"]');
585
+ if (!contentElement) return false;
586
+
587
+ let currentHandler: PopoverHandler | null =
588
+ ((contentElement as any).__popoverHandler as PopoverHandler | undefined) ?? null;
589
+
590
+ while (currentHandler) {
591
+ if (currentHandler === this) {
592
+ return true;
593
+ }
594
+
595
+ currentHandler = currentHandler.parentHandler;
596
+ }
597
+
598
+ return false;
599
+ }
600
+
601
+ private portalContent() {
602
+ if (!this.content) return;
603
+ if (this.contentPlaceholder || this.content.parentElement === document.body) return;
604
+
605
+ this.contentPlaceholder = document.createComment("popover-content-placeholder");
606
+ this.content.parentNode?.insertBefore(this.contentPlaceholder, this.content);
607
+
608
+ document.body.appendChild(this.content);
609
+
610
+ this.content.style.position = "fixed";
611
+ this.content.style.zIndex = "50";
612
+
613
+ const updatePosition = () => this.positionContent();
614
+ window.addEventListener("scroll", updatePosition, true);
615
+ window.addEventListener("resize", updatePosition);
616
+
617
+ const resizeObserver = new ResizeObserver(() => this.positionContent());
618
+ resizeObserver.observe(this.content);
619
+ if (this.trigger) {
620
+ resizeObserver.observe(this.trigger);
621
+ }
622
+
623
+ this.cleanupAutoUpdate = () => {
624
+ window.removeEventListener("scroll", updatePosition, true);
625
+ window.removeEventListener("resize", updatePosition);
626
+ resizeObserver.disconnect();
627
+ };
628
+ }
629
+
630
+ private unportalContent() {
631
+ if (!this.content) return;
632
+
633
+ this.cleanupAutoUpdate?.();
634
+ this.cleanupAutoUpdate = null;
635
+
636
+ if (this.contentPlaceholder) {
637
+ this.contentPlaceholder.parentNode?.insertBefore(this.content, this.contentPlaceholder);
638
+ this.contentPlaceholder.remove();
639
+ this.contentPlaceholder = null;
640
+ }
641
+
642
+ this.content.style.removeProperty("position");
643
+ this.content.style.removeProperty("z-index");
644
+ this.content.style.removeProperty("top");
645
+ this.content.style.removeProperty("left");
646
+ this.content.style.removeProperty("transform-origin");
647
+ }
648
+
649
+ private positionContent() {
650
+ if (!this.content || !this.trigger || !this.isOpen) return;
651
+
652
+ const triggerRect = this.trigger.getBoundingClientRect();
653
+ const contentWidth = this.content.offsetWidth;
654
+ const contentHeight = this.content.offsetHeight;
655
+ const viewportWidth = window.innerWidth;
656
+ const viewportHeight = window.innerHeight;
657
+ const viewportPadding = 8;
658
+
659
+ const resolvedPlacement = resolvePlacement({
660
+ side: this.side,
661
+ align: this.align,
662
+ sideOffset: this.sideOffset,
663
+ triggerRect,
664
+ contentWidth,
665
+ contentHeight,
666
+ viewportWidth,
667
+ viewportHeight,
668
+ viewportPadding,
669
+ avoidCollisions: true,
670
+ });
671
+
672
+ this.content.style.left = `${Math.round(resolvedPlacement.left)}px`;
673
+ this.content.style.top = `${Math.round(resolvedPlacement.top)}px`;
674
+ this.content.style.transformOrigin = getTransformOrigin(
675
+ resolvedPlacement.side,
676
+ resolvedPlacement.align,
677
+ );
678
+ this.content.setAttribute("data-side", resolvedPlacement.side);
679
+ this.content.setAttribute("data-align", resolvedPlacement.align);
680
+ }
681
+ }
682
+
683
+ const popoverInstances = new Map<HTMLElement, PopoverHandler>();
684
+ let popoverCounter = 0;
685
+
686
+ const cleanupStalePopovers = () => {
687
+ for (const [popoverEl, popoverHandler] of popoverInstances.entries()) {
688
+ if (!document.contains(popoverEl)) {
689
+ popoverHandler.destroy();
690
+ popoverInstances.delete(popoverEl);
691
+ }
692
+ }
693
+ };
694
+
695
+ const destroyAllPopovers = () => {
696
+ for (const popoverHandler of popoverInstances.values()) {
697
+ popoverHandler.destroy();
698
+ }
699
+
700
+ popoverInstances.clear();
701
+ };
702
+
703
+ const initPopovers = () => {
704
+ cleanupStalePopovers();
705
+
706
+ document.querySelectorAll<HTMLElement>(".starwind-popover").forEach((popoverEl) => {
707
+ if (!popoverInstances.has(popoverEl)) {
708
+ popoverInstances.set(popoverEl, new PopoverHandler(popoverEl, popoverCounter++));
709
+ }
710
+ });
711
+ };
712
+
713
+ initPopovers();
714
+ document.addEventListener("astro:before-swap", destroyAllPopovers);
715
+ document.addEventListener("astro:after-swap", initPopovers);
716
+ document.addEventListener("starwind:init", initPopovers);
717
+ </script>