@starwind-ui/core 1.15.1 → 1.15.2

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 (216) hide show
  1. package/dist/index.d.ts +28 -0
  2. package/dist/index.js +108 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/src/components/accordion/Accordion.astro +254 -0
  5. package/dist/src/components/accordion/AccordionContent.astro +33 -0
  6. package/dist/src/components/accordion/AccordionItem.astro +27 -0
  7. package/dist/src/components/accordion/AccordionTrigger.astro +32 -0
  8. package/dist/src/components/accordion/index.ts +15 -0
  9. package/dist/src/components/alert/Alert.astro +31 -0
  10. package/dist/src/components/alert/AlertDescription.astro +14 -0
  11. package/dist/src/components/alert/AlertTitle.astro +16 -0
  12. package/dist/src/components/alert/index.ts +13 -0
  13. package/dist/src/components/alert-dialog/AlertDialog.astro +275 -0
  14. package/dist/src/components/alert-dialog/AlertDialogAction.astro +44 -0
  15. package/dist/src/components/alert-dialog/AlertDialogCancel.astro +45 -0
  16. package/dist/src/components/alert-dialog/AlertDialogContent.astro +52 -0
  17. package/dist/src/components/alert-dialog/AlertDialogDescription.astro +18 -0
  18. package/dist/src/components/alert-dialog/AlertDialogFooter.astro +16 -0
  19. package/dist/src/components/alert-dialog/AlertDialogHeader.astro +14 -0
  20. package/dist/src/components/alert-dialog/AlertDialogTitle.astro +20 -0
  21. package/dist/src/components/alert-dialog/AlertDialogTrigger.astro +47 -0
  22. package/dist/src/components/alert-dialog/index.ts +46 -0
  23. package/dist/src/components/aspect-ratio/AspectRatio.astro +32 -0
  24. package/dist/src/components/aspect-ratio/index.ts +7 -0
  25. package/dist/src/components/avatar/Avatar.astro +29 -0
  26. package/dist/src/components/avatar/AvatarFallback.astro +18 -0
  27. package/dist/src/components/avatar/AvatarImage.astro +49 -0
  28. package/dist/src/components/avatar/index.ts +13 -0
  29. package/dist/src/components/badge/Badge.astro +55 -0
  30. package/dist/src/components/badge/index.ts +7 -0
  31. package/dist/src/components/breadcrumb/Breadcrumb.astro +11 -0
  32. package/dist/src/components/breadcrumb/BreadcrumbEllipsis.astro +28 -0
  33. package/dist/src/components/breadcrumb/BreadcrumbItem.astro +14 -0
  34. package/dist/src/components/breadcrumb/BreadcrumbLink.astro +22 -0
  35. package/dist/src/components/breadcrumb/BreadcrumbList.astro +16 -0
  36. package/dist/src/components/breadcrumb/BreadcrumbPage.astro +21 -0
  37. package/dist/src/components/breadcrumb/BreadcrumbSeparator.astro +23 -0
  38. package/dist/src/components/breadcrumb/index.ts +37 -0
  39. package/dist/src/components/button/Button.astro +55 -0
  40. package/dist/src/components/button/index.ts +7 -0
  41. package/dist/src/components/button-group/ButtonGroup.astro +62 -0
  42. package/dist/src/components/button-group/ButtonGroupSeparator.astro +27 -0
  43. package/dist/src/components/button-group/ButtonGroupText.astro +19 -0
  44. package/dist/src/components/button-group/index.ts +17 -0
  45. package/dist/src/components/card/Card.astro +14 -0
  46. package/dist/src/components/card/CardContent.astro +14 -0
  47. package/dist/src/components/card/CardDescription.astro +14 -0
  48. package/dist/src/components/card/CardFooter.astro +14 -0
  49. package/dist/src/components/card/CardHeader.astro +14 -0
  50. package/dist/src/components/card/CardTitle.astro +14 -0
  51. package/dist/src/components/card/index.ts +26 -0
  52. package/dist/src/components/carousel/Carousel.astro +55 -0
  53. package/dist/src/components/carousel/CarouselContent.astro +26 -0
  54. package/dist/src/components/carousel/CarouselItem.astro +26 -0
  55. package/dist/src/components/carousel/CarouselNext.astro +37 -0
  56. package/dist/src/components/carousel/CarouselPrevious.astro +37 -0
  57. package/dist/src/components/carousel/carousel-script.ts +191 -0
  58. package/dist/src/components/carousel/index.ts +32 -0
  59. package/dist/src/components/checkbox/Checkbox.astro +128 -0
  60. package/dist/src/components/checkbox/index.ts +7 -0
  61. package/dist/src/components/collapsible/Collapsible.astro +161 -0
  62. package/dist/src/components/collapsible/CollapsibleContent.astro +22 -0
  63. package/dist/src/components/collapsible/CollapsibleTrigger.astro +44 -0
  64. package/dist/src/components/collapsible/index.ts +13 -0
  65. package/dist/src/components/dialog/Dialog.astro +389 -0
  66. package/dist/src/components/dialog/DialogClose.astro +35 -0
  67. package/dist/src/components/dialog/DialogContent.astro +78 -0
  68. package/dist/src/components/dialog/DialogDescription.astro +14 -0
  69. package/dist/src/components/dialog/DialogFooter.astro +14 -0
  70. package/dist/src/components/dialog/DialogHeader.astro +14 -0
  71. package/dist/src/components/dialog/DialogTitle.astro +22 -0
  72. package/dist/src/components/dialog/DialogTrigger.astro +47 -0
  73. package/dist/src/components/dialog/index.ts +45 -0
  74. package/dist/src/components/dropdown/Dropdown.astro +377 -0
  75. package/dist/src/components/dropdown/DropdownContent.astro +81 -0
  76. package/dist/src/components/dropdown/DropdownItem.astro +48 -0
  77. package/dist/src/components/dropdown/DropdownLabel.astro +29 -0
  78. package/dist/src/components/dropdown/DropdownSeparator.astro +21 -0
  79. package/dist/src/components/dropdown/DropdownTrigger.astro +52 -0
  80. package/dist/src/components/dropdown/index.ts +33 -0
  81. package/dist/src/components/dropzone/Dropzone.astro +236 -0
  82. package/dist/src/components/dropzone/DropzoneFilesList.astro +26 -0
  83. package/dist/src/components/dropzone/DropzoneLoadingIndicator.astro +10 -0
  84. package/dist/src/components/dropzone/DropzoneUploadIndicator.astro +10 -0
  85. package/dist/src/components/dropzone/index.ts +24 -0
  86. package/dist/src/components/image/Image.astro +24 -0
  87. package/dist/src/components/image/index.ts +9 -0
  88. package/dist/src/components/input/Input.astro +25 -0
  89. package/dist/src/components/input/index.ts +7 -0
  90. package/dist/src/components/input-otp/InputOtp.astro +352 -0
  91. package/dist/src/components/input-otp/InputOtpGroup.astro +16 -0
  92. package/dist/src/components/input-otp/InputOtpSeparator.astro +25 -0
  93. package/dist/src/components/input-otp/InputOtpSlot.astro +48 -0
  94. package/dist/src/components/input-otp/InputOtpTypes.ts +6 -0
  95. package/dist/src/components/input-otp/index.ts +33 -0
  96. package/dist/src/components/item/Item.astro +52 -0
  97. package/dist/src/components/item/ItemActions.astro +16 -0
  98. package/dist/src/components/item/ItemContent.astro +16 -0
  99. package/dist/src/components/item/ItemDescription.astro +19 -0
  100. package/dist/src/components/item/ItemFooter.astro +16 -0
  101. package/dist/src/components/item/ItemGroup.astro +16 -0
  102. package/dist/src/components/item/ItemHeader.astro +16 -0
  103. package/dist/src/components/item/ItemMedia.astro +40 -0
  104. package/dist/src/components/item/ItemSeparator.astro +21 -0
  105. package/dist/src/components/item/ItemTitle.astro +16 -0
  106. package/dist/src/components/item/index.ts +50 -0
  107. package/dist/src/components/kbd/Kbd.astro +21 -0
  108. package/dist/src/components/kbd/KbdGroup.astro +16 -0
  109. package/dist/src/components/kbd/index.ts +11 -0
  110. package/dist/src/components/label/Label.astro +22 -0
  111. package/dist/src/components/label/index.ts +7 -0
  112. package/dist/src/components/pagination/Pagination.astro +20 -0
  113. package/dist/src/components/pagination/PaginationContent.astro +16 -0
  114. package/dist/src/components/pagination/PaginationEllipsis.astro +35 -0
  115. package/dist/src/components/pagination/PaginationItem.astro +16 -0
  116. package/dist/src/components/pagination/PaginationLink.astro +24 -0
  117. package/dist/src/components/pagination/PaginationNext.astro +30 -0
  118. package/dist/src/components/pagination/PaginationPrevious.astro +30 -0
  119. package/dist/src/components/pagination/index.ts +38 -0
  120. package/dist/src/components/progress/Progress.astro +155 -0
  121. package/dist/src/components/progress/index.ts +10 -0
  122. package/dist/src/components/prose/Prose.astro +617 -0
  123. package/dist/src/components/prose/index.ts +9 -0
  124. package/dist/src/components/radio-group/RadioGroup.astro +162 -0
  125. package/dist/src/components/radio-group/RadioGroupItem.astro +129 -0
  126. package/dist/src/components/radio-group/RadioGroupTypes.ts +6 -0
  127. package/dist/src/components/radio-group/index.ts +23 -0
  128. package/dist/src/components/select/Select.astro +752 -0
  129. package/dist/src/components/select/SelectContent.astro +94 -0
  130. package/dist/src/components/select/SelectGroup.astro +9 -0
  131. package/dist/src/components/select/SelectItem.astro +51 -0
  132. package/dist/src/components/select/SelectLabel.astro +14 -0
  133. package/dist/src/components/select/SelectSearch.astro +49 -0
  134. package/dist/src/components/select/SelectSeparator.astro +12 -0
  135. package/dist/src/components/select/SelectTrigger.astro +54 -0
  136. package/dist/src/components/select/SelectTypes.ts +13 -0
  137. package/dist/src/components/select/SelectValue.astro +19 -0
  138. package/dist/src/components/select/index.ts +49 -0
  139. package/dist/src/components/separator/Separator.astro +36 -0
  140. package/dist/src/components/separator/index.ts +7 -0
  141. package/dist/src/components/sheet/Sheet.astro +13 -0
  142. package/dist/src/components/sheet/SheetClose.astro +13 -0
  143. package/dist/src/components/sheet/SheetContent.astro +92 -0
  144. package/dist/src/components/sheet/SheetDescription.astro +16 -0
  145. package/dist/src/components/sheet/SheetFooter.astro +16 -0
  146. package/dist/src/components/sheet/SheetHeader.astro +16 -0
  147. package/dist/src/components/sheet/SheetTitle.astro +16 -0
  148. package/dist/src/components/sheet/SheetTrigger.astro +13 -0
  149. package/dist/src/components/sheet/index.ts +41 -0
  150. package/dist/src/components/sidebar/Sidebar.astro +213 -0
  151. package/dist/src/components/sidebar/SidebarContent.astro +24 -0
  152. package/dist/src/components/sidebar/SidebarFooter.astro +21 -0
  153. package/dist/src/components/sidebar/SidebarGroup.astro +21 -0
  154. package/dist/src/components/sidebar/SidebarGroupContent.astro +21 -0
  155. package/dist/src/components/sidebar/SidebarGroupLabel.astro +52 -0
  156. package/dist/src/components/sidebar/SidebarHeader.astro +21 -0
  157. package/dist/src/components/sidebar/SidebarInput.astro +22 -0
  158. package/dist/src/components/sidebar/SidebarInset.astro +21 -0
  159. package/dist/src/components/sidebar/SidebarMenu.astro +21 -0
  160. package/dist/src/components/sidebar/SidebarMenuAction.astro +59 -0
  161. package/dist/src/components/sidebar/SidebarMenuBadge.astro +30 -0
  162. package/dist/src/components/sidebar/SidebarMenuButton.astro +129 -0
  163. package/dist/src/components/sidebar/SidebarMenuItem.astro +21 -0
  164. package/dist/src/components/sidebar/SidebarMenuSkeleton.astro +40 -0
  165. package/dist/src/components/sidebar/SidebarMenuSub.astro +24 -0
  166. package/dist/src/components/sidebar/SidebarMenuSubButton.astro +49 -0
  167. package/dist/src/components/sidebar/SidebarMenuSubItem.astro +16 -0
  168. package/dist/src/components/sidebar/SidebarProvider.astro +213 -0
  169. package/dist/src/components/sidebar/SidebarRail.astro +71 -0
  170. package/dist/src/components/sidebar/SidebarSeparator.astro +22 -0
  171. package/dist/src/components/sidebar/SidebarTrigger.astro +66 -0
  172. package/dist/src/components/sidebar/index.ts +103 -0
  173. package/dist/src/components/skeleton/Skeleton.astro +14 -0
  174. package/dist/src/components/skeleton/index.ts +9 -0
  175. package/dist/src/components/slider/Slider.astro +411 -0
  176. package/dist/src/components/slider/index.ts +9 -0
  177. package/dist/src/components/spinner/Spinner.astro +21 -0
  178. package/dist/src/components/spinner/index.ts +7 -0
  179. package/dist/src/components/switch/Switch.astro +192 -0
  180. package/dist/src/components/switch/SwitchTypes.ts +6 -0
  181. package/dist/src/components/switch/index.ts +12 -0
  182. package/dist/src/components/table/Table.astro +18 -0
  183. package/dist/src/components/table/TableBody.astro +16 -0
  184. package/dist/src/components/table/TableCaption.astro +16 -0
  185. package/dist/src/components/table/TableCell.astro +16 -0
  186. package/dist/src/components/table/TableFoot.astro +16 -0
  187. package/dist/src/components/table/TableHead.astro +16 -0
  188. package/dist/src/components/table/TableHeader.astro +16 -0
  189. package/dist/src/components/table/TableRow.astro +16 -0
  190. package/dist/src/components/table/index.ts +42 -0
  191. package/dist/src/components/tabs/Tabs.astro +271 -0
  192. package/dist/src/components/tabs/TabsContent.astro +28 -0
  193. package/dist/src/components/tabs/TabsList.astro +22 -0
  194. package/dist/src/components/tabs/TabsTrigger.astro +34 -0
  195. package/dist/src/components/tabs/index.ts +20 -0
  196. package/dist/src/components/textarea/Textarea.astro +29 -0
  197. package/dist/src/components/textarea/index.ts +9 -0
  198. package/dist/src/components/theme-toggle/ThemeToggle.astro +208 -0
  199. package/dist/src/components/theme-toggle/index.ts +7 -0
  200. package/dist/src/components/toast/ToastDescription.astro +21 -0
  201. package/dist/src/components/toast/ToastItem.astro +54 -0
  202. package/dist/src/components/toast/ToastTemplate.astro +25 -0
  203. package/dist/src/components/toast/ToastTitle.astro +57 -0
  204. package/dist/src/components/toast/Toaster.astro +982 -0
  205. package/dist/src/components/toast/index.ts +29 -0
  206. package/dist/src/components/toast/toast-manager.ts +216 -0
  207. package/dist/src/components/toggle/Toggle.astro +174 -0
  208. package/dist/src/components/toggle/ToggleTypes.ts +14 -0
  209. package/dist/src/components/toggle/index.ts +8 -0
  210. package/dist/src/components/tooltip/Tooltip.astro +282 -0
  211. package/dist/src/components/tooltip/TooltipContent.astro +89 -0
  212. package/dist/src/components/tooltip/TooltipTrigger.astro +10 -0
  213. package/dist/src/components/tooltip/index.ts +16 -0
  214. package/dist/src/components/video/Video.astro +120 -0
  215. package/dist/src/components/video/index.ts +9 -0
  216. package/package.json +1 -1
@@ -0,0 +1,282 @@
1
+ ---
2
+ import type { HTMLAttributes } from "astro/types";
3
+ import { tv } from "tailwind-variants";
4
+
5
+ type Props = HTMLAttributes<"div"> & {
6
+ /**
7
+ * Time in milliseconds to wait before showing the tooltip
8
+ */
9
+ openDelay?: number;
10
+ /**
11
+ * Time in milliseconds to wait before hiding the tooltip
12
+ */
13
+ closeDelay?: number;
14
+ /**
15
+ * When true, prevents the tooltip from staying open when hovering over its content
16
+ */
17
+ disableHoverableContent?: boolean;
18
+ };
19
+
20
+ export const tooltip = tv({ base: "starwind-tooltip relative inline-block" });
21
+
22
+ const {
23
+ openDelay = 200,
24
+ closeDelay = 200,
25
+ disableHoverableContent = false,
26
+ class: className,
27
+ } = Astro.props;
28
+ ---
29
+
30
+ <div
31
+ class={tooltip({ class: className })}
32
+ data-slot="tooltip"
33
+ data-state="closed"
34
+ data-open-delay={openDelay}
35
+ data-close-delay={closeDelay}
36
+ {...!disableHoverableContent && { "data-content-hoverable": "" }}
37
+ >
38
+ <slot />
39
+ </div>
40
+
41
+ <script>
42
+ class TooltipHandler {
43
+ private tooltip: HTMLElement;
44
+ private trigger: HTMLElement | null;
45
+ private content: HTMLElement | null;
46
+ private openTimerRef: number | null = null;
47
+ private closeTimerRef: number | null = null;
48
+ private contentId: string;
49
+ private animationDuration = 150;
50
+
51
+ constructor(tooltip: HTMLElement, idx: number) {
52
+ this.contentId = `starwind-tooltip${idx}`;
53
+ this.tooltip = tooltip;
54
+ this.content = tooltip.querySelector(".starwind-tooltip-content");
55
+
56
+ // if tooltip.firstElementChild is this.content, then get the next element
57
+ this.trigger = tooltip.firstElementChild as HTMLElement;
58
+ if (this.trigger === this.content) {
59
+ this.trigger = this.trigger.nextElementSibling as HTMLElement;
60
+ }
61
+
62
+ this.trigger.classList.add("starwind-tooltip-trigger");
63
+
64
+ if (!this.trigger || !this.content) return;
65
+
66
+ // animationDuration is set with inline styles through passed prop to TooltipContent
67
+ const animationDurationString = this.content.style.animationDuration;
68
+ if (animationDurationString.endsWith("ms")) {
69
+ this.animationDuration = parseFloat(animationDurationString);
70
+ } else if (animationDurationString.endsWith("s")) {
71
+ // using something like @playform/compress might optimize to use "s" instead of "ms"
72
+ this.animationDuration = parseFloat(animationDurationString) * 1000;
73
+ }
74
+ this.init();
75
+ }
76
+
77
+ private init() {
78
+ this.setupAccessibility();
79
+ this.setupEvents();
80
+ }
81
+
82
+ private setupAccessibility() {
83
+ if (!this.trigger || !this.content) return;
84
+ this.trigger.setAttribute("aria-describedby", this.contentId);
85
+ this.content.id = this.contentId;
86
+ }
87
+
88
+ private setupEvents() {
89
+ if (!this.trigger || !this.content) return;
90
+
91
+ // Trigger events
92
+ this.trigger.addEventListener("mouseenter", () => this.show());
93
+ this.trigger.addEventListener("mouseleave", () => this.hide());
94
+ this.trigger.addEventListener("focus", () => this.show(true));
95
+ this.trigger.addEventListener("blur", () => this.hide(true));
96
+
97
+ // Content events
98
+ if (this.tooltip.hasAttribute("data-content-hoverable")) {
99
+ this.content.addEventListener("mouseenter", () => this.show());
100
+ this.content.addEventListener("mouseleave", () => this.hide());
101
+ }
102
+
103
+ // Document events
104
+ document.addEventListener("keydown", (e) => {
105
+ if (e.key === "Escape" && this.tooltip.getAttribute("data-state") === "open") {
106
+ this.hide(true);
107
+ }
108
+ });
109
+
110
+ document.addEventListener("click", (e) => {
111
+ if (
112
+ !this.tooltip.contains(e.target as Node) &&
113
+ this.tooltip.getAttribute("data-state") === "open"
114
+ ) {
115
+ this.hide(true);
116
+ }
117
+ });
118
+ }
119
+
120
+ private show(immediate: boolean = false) {
121
+ if (!this.content || !this.trigger) return;
122
+ if (immediate) {
123
+ this.tooltip.setAttribute("data-state", "open");
124
+ this.content.setAttribute("data-state", "open");
125
+ this.content.style.display = "block";
126
+ this.positionTooltip();
127
+ this.clearOpenTimer();
128
+ return;
129
+ }
130
+
131
+ this.clearCloseTimer();
132
+
133
+ const delay = parseInt(this.tooltip.getAttribute("data-open-delay") || "700");
134
+ this.openTimerRef = window.setTimeout(() => {
135
+ if (!this.content || !this.trigger) return;
136
+ this.tooltip.setAttribute("data-state", "open");
137
+ this.content.setAttribute("data-state", "open");
138
+ this.content.style.display = "block";
139
+ this.positionTooltip();
140
+ this.openTimerRef = null;
141
+ }, delay);
142
+ }
143
+
144
+ private positionTooltip() {
145
+ if (!this.content || !this.trigger) return;
146
+
147
+ const triggerRect = this.trigger.getBoundingClientRect();
148
+ const contentRect = this.content.getBoundingClientRect();
149
+ const side = this.content.dataset.side || "top";
150
+ const align = this.content.dataset.align || "center";
151
+ const sideOffset = parseInt(this.content.dataset.sideOffset || "8");
152
+
153
+ let top = 0;
154
+ let left = 0;
155
+
156
+ // Calculate position based on side
157
+ switch (side) {
158
+ case "top":
159
+ top = triggerRect.top - contentRect.height - sideOffset;
160
+ break;
161
+ case "bottom":
162
+ top = triggerRect.bottom + sideOffset;
163
+ break;
164
+ case "left":
165
+ left = triggerRect.left - contentRect.width - sideOffset;
166
+ break;
167
+ case "right":
168
+ left = triggerRect.right + sideOffset;
169
+ break;
170
+ }
171
+
172
+ // Calculate alignment
173
+ if (side === "top" || side === "bottom") {
174
+ switch (align) {
175
+ case "center":
176
+ left = triggerRect.left + (triggerRect.width - contentRect.width) / 2;
177
+ break;
178
+ case "start":
179
+ left = triggerRect.left;
180
+ break;
181
+ case "end":
182
+ left = triggerRect.right - contentRect.width;
183
+ break;
184
+ }
185
+ } else {
186
+ switch (align) {
187
+ case "center":
188
+ top = triggerRect.top + (triggerRect.height - contentRect.height) / 2;
189
+ break;
190
+ case "start":
191
+ top = triggerRect.top;
192
+ break;
193
+ case "end":
194
+ top = triggerRect.bottom - contentRect.height;
195
+ break;
196
+ }
197
+ }
198
+
199
+ // Apply collision avoidance if enabled
200
+ if (this.content.hasAttribute("data-avoid-collisions")) {
201
+ const padding = 8;
202
+ const viewportWidth = window.innerWidth;
203
+ const viewportHeight = window.innerHeight;
204
+
205
+ // Horizontal bounds
206
+ if (left < padding) left = padding;
207
+ if (left + contentRect.width > viewportWidth - padding) {
208
+ left = viewportWidth - contentRect.width - padding;
209
+ }
210
+
211
+ // Vertical bounds
212
+ if (top < padding) top = padding;
213
+ if (top + contentRect.height > viewportHeight - padding) {
214
+ top = viewportHeight - contentRect.height - padding;
215
+ }
216
+ }
217
+
218
+ this.content.style.top = `${top}px`;
219
+ this.content.style.left = `${left}px`;
220
+ }
221
+
222
+ private hide(immediate: boolean = false) {
223
+ if (!this.content || !this.trigger) return;
224
+ this.clearOpenTimer();
225
+
226
+ if (immediate) {
227
+ this.clearCloseTimer();
228
+ this.tooltip.setAttribute("data-state", "closed");
229
+ setTimeout(() => {
230
+ if (!this.content) return;
231
+ this.content.style.display = "none";
232
+ }, this.animationDuration);
233
+ this.content.setAttribute("data-state", "closed");
234
+ return;
235
+ }
236
+
237
+ this.closeTimerRef = window.setTimeout(
238
+ () => {
239
+ this.tooltip.setAttribute("data-state", "closed");
240
+ setTimeout(() => {
241
+ if (!this.content) return;
242
+ this.content.style.display = "none";
243
+ }, this.animationDuration);
244
+ if (!this.content) return;
245
+ this.content.setAttribute("data-state", "closed");
246
+ this.closeTimerRef = null;
247
+ },
248
+ parseInt(this.tooltip.getAttribute("data-close-delay") || "300"),
249
+ );
250
+ }
251
+
252
+ private clearOpenTimer() {
253
+ if (this.openTimerRef) {
254
+ window.clearTimeout(this.openTimerRef);
255
+ this.openTimerRef = null;
256
+ }
257
+ }
258
+
259
+ private clearCloseTimer() {
260
+ if (this.closeTimerRef) {
261
+ window.clearTimeout(this.closeTimerRef);
262
+ this.closeTimerRef = null;
263
+ }
264
+ }
265
+ }
266
+
267
+ // Store instances in a WeakMap to avoid memory leaks
268
+ const tooltipInstances = new WeakMap<HTMLElement, TooltipHandler>();
269
+ let tooltipCounter = 0;
270
+
271
+ const setupTooltips = () => {
272
+ document.querySelectorAll<HTMLElement>(".starwind-tooltip").forEach((tooltip) => {
273
+ if (!tooltipInstances.has(tooltip)) {
274
+ tooltipInstances.set(tooltip, new TooltipHandler(tooltip, tooltipCounter++));
275
+ }
276
+ });
277
+ };
278
+
279
+ setupTooltips();
280
+ document.addEventListener("astro:after-swap", setupTooltips);
281
+ document.addEventListener("starwind:init", setupTooltips);
282
+ </script>
@@ -0,0 +1,89 @@
1
+ ---
2
+ import CaretUp from "@tabler/icons/filled/caret-up.svg";
3
+ import type { HTMLAttributes } from "astro/types";
4
+ import { tv } from "tailwind-variants";
5
+
6
+ type Props = HTMLAttributes<"div"> & {
7
+ /**
8
+ * Side of the tooltip
9
+ * @default top
10
+ */
11
+ side?: "top" | "right" | "bottom" | "left";
12
+ /**
13
+ * Alignment of the tooltip
14
+ * @default center
15
+ */
16
+ align?: "start" | "center" | "end";
17
+ /**
18
+ * Offset distance in pixels
19
+ * @default 8
20
+ */
21
+ sideOffset?: number;
22
+ /**
23
+ * Prevent the tooltip from colliding with other elements
24
+ * @default true
25
+ */
26
+ avoidCollisions?: boolean;
27
+ /**
28
+ * Open and close animation duration in milliseconds
29
+ * @default 150
30
+ */
31
+ animationDuration?: number;
32
+ };
33
+
34
+ export const tooltipContent = tv({
35
+ base: [
36
+ "starwind-tooltip-content",
37
+ "fixed z-50 hidden w-fit px-3 py-1.5",
38
+ "bg-foreground text-background rounded-md",
39
+ "animate-in fade-in zoom-in-95",
40
+ "data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fade-out zoom-out-95",
41
+ ],
42
+ variants: {
43
+ side: {
44
+ left: "slide-in-from-right-2",
45
+ right: "slide-in-from-left-2",
46
+ bottom: "slide-in-from-top-2",
47
+ top: "slide-in-from-bottom-2",
48
+ },
49
+ },
50
+ defaultVariants: { side: "top" },
51
+ });
52
+
53
+ export const tooltipCaret = tv({
54
+ base: ["text-foreground absolute z-50 size-4"],
55
+ variants: {
56
+ side: {
57
+ top: "bottom-0 left-1/2 -translate-x-1/2 translate-y-[calc(50%+1px)] rotate-180",
58
+ bottom: "top-0 left-1/2 -translate-x-1/2 -translate-y-[calc(50%+1px)]",
59
+ left: "top-1/2 right-0 translate-x-[calc(50%+1px)] -translate-y-1/2 rotate-90",
60
+ right: "top-1/2 left-0 -translate-x-[calc(50%+1px)] -translate-y-1/2 -rotate-90",
61
+ },
62
+ },
63
+ defaultVariants: { side: "top" },
64
+ });
65
+
66
+ const {
67
+ side = "top",
68
+ align = "center",
69
+ sideOffset = 8,
70
+ avoidCollisions = true,
71
+ animationDuration = 150,
72
+ class: className,
73
+ } = Astro.props;
74
+ ---
75
+
76
+ <div
77
+ class={tooltipContent({ side, class: className })}
78
+ data-slot="tooltip-content"
79
+ data-state="closed"
80
+ data-side={side}
81
+ data-align={align}
82
+ data-side-offset={sideOffset}
83
+ {...avoidCollisions && { "data-avoid-collisions": "" }}
84
+ role="tooltip"
85
+ style={{ animationDuration: `${animationDuration}ms` }}
86
+ >
87
+ <slot> My tooltip! </slot>
88
+ <CaretUp class={tooltipCaret({ side })} />
89
+ </div>
@@ -0,0 +1,10 @@
1
+ ---
2
+ /**
3
+ * This component is used to trigger the tooltip
4
+ * All it does is require a slot
5
+ */
6
+
7
+ type Props = { children: any };
8
+ ---
9
+
10
+ <slot />
@@ -0,0 +1,16 @@
1
+ import Tooltip, { tooltip } from "./Tooltip.astro";
2
+ import TooltipContent, { tooltipContent } from "./TooltipContent.astro";
3
+ import TooltipTrigger from "./TooltipTrigger.astro";
4
+
5
+ const TooltipVariants = {
6
+ tooltip,
7
+ tooltipContent,
8
+ };
9
+
10
+ export { Tooltip, TooltipContent, TooltipTrigger, TooltipVariants };
11
+
12
+ export default {
13
+ Root: Tooltip,
14
+ Trigger: TooltipTrigger,
15
+ Content: TooltipContent,
16
+ };
@@ -0,0 +1,120 @@
1
+ ---
2
+ import type { HTMLAttributes } from "astro/types";
3
+ import { tv } from "tailwind-variants";
4
+
5
+ export const video = tv({
6
+ base: "starwind-video aspect-video h-auto w-full",
7
+ });
8
+
9
+ type Props = HTMLAttributes<"video"> &
10
+ HTMLAttributes<"iframe"> & {
11
+ src: string;
12
+ title?: string;
13
+ autoplay?: boolean;
14
+ muted?: boolean;
15
+ loop?: boolean;
16
+ controls?: boolean;
17
+ poster?: string;
18
+ };
19
+
20
+ const {
21
+ class: className,
22
+ src,
23
+ title = "Video",
24
+ autoplay = false,
25
+ muted = false,
26
+ loop = false,
27
+ controls = true,
28
+ poster,
29
+ ...rest
30
+ } = Astro.props;
31
+
32
+ /**
33
+ * Detects the video type from the src URL
34
+ */
35
+ function getVideoType(url: string): "youtube" | "youtube-shorts" | "native" {
36
+ if (url.includes("youtube.com/shorts/") || url.includes("youtu.be/shorts/")) {
37
+ return "youtube-shorts";
38
+ }
39
+ if (
40
+ url.includes("youtube.com") ||
41
+ url.includes("youtu.be") ||
42
+ url.includes("youtube-nocookie.com")
43
+ ) {
44
+ return "youtube";
45
+ }
46
+ return "native";
47
+ }
48
+
49
+ /**
50
+ * Extracts YouTube video ID from various URL formats
51
+ */
52
+ function getYouTubeId(url: string): string | null {
53
+ const patterns = [
54
+ /youtube\.com\/shorts\/([^?&]+)/,
55
+ /youtube\.com\/watch\?v=([^&]+)/,
56
+ /youtube\.com\/embed\/([^?&]+)/,
57
+ /youtu\.be\/([^?&]+)/,
58
+ /youtube-nocookie\.com\/embed\/([^?&]+)/,
59
+ ];
60
+
61
+ for (const pattern of patterns) {
62
+ const match = url.match(pattern);
63
+ if (match) return match[1];
64
+ }
65
+ return null;
66
+ }
67
+
68
+ /**
69
+ * Builds YouTube embed URL with parameters
70
+ */
71
+ function buildYouTubeEmbedUrl(videoId: string, isShort: boolean): string {
72
+ const params = new URLSearchParams();
73
+ if (autoplay) params.set("autoplay", "1");
74
+ if (muted) params.set("mute", "1");
75
+ if (loop) {
76
+ params.set("loop", "1");
77
+ params.set("playlist", videoId);
78
+ }
79
+ if (!controls) params.set("controls", "0");
80
+
81
+ const baseUrl = `https://www.youtube-nocookie.com/embed/${videoId}`;
82
+ const queryString = params.toString();
83
+ return queryString ? `${baseUrl}?${queryString}` : baseUrl;
84
+ }
85
+
86
+ const videoType = getVideoType(src);
87
+ const youtubeId = videoType !== "native" ? getYouTubeId(src) : null;
88
+ const isShort = videoType === "youtube-shorts";
89
+ const embedUrl = youtubeId ? buildYouTubeEmbedUrl(youtubeId, isShort) : null;
90
+ ---
91
+
92
+ {
93
+ videoType === "native" || !embedUrl ? (
94
+ <video
95
+ class={video({ class: className })}
96
+ src={src}
97
+ autoplay={autoplay}
98
+ muted={muted}
99
+ loop={loop}
100
+ controls={controls}
101
+ poster={poster}
102
+ data-slot="video"
103
+ {...rest}
104
+ >
105
+ <track kind="captions" />
106
+ </video>
107
+ ) : (
108
+ <iframe
109
+ class={video({ class: className })}
110
+ src={embedUrl}
111
+ title={title}
112
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
113
+ referrerpolicy="strict-origin-when-cross-origin"
114
+ allowfullscreen
115
+ data-slot="video"
116
+ data-video-type={isShort ? "youtube-shorts" : "youtube"}
117
+ {...rest}
118
+ />
119
+ )
120
+ }
@@ -0,0 +1,9 @@
1
+ import Video, { video } from "./Video.astro";
2
+
3
+ const VideoVariants = {
4
+ video,
5
+ };
6
+
7
+ export { Video, VideoVariants };
8
+
9
+ export default Video;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starwind-ui/core",
3
- "version": "1.15.1",
3
+ "version": "1.15.2",
4
4
  "description": "Starwind UI core components and registry",
5
5
  "license": "MIT",
6
6
  "author": {