@starwind-ui/core 1.6.2 → 1.7.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 (101) hide show
  1. package/dist/index.js +8 -4
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/components/accordion/Accordion.astro +224 -224
  4. package/dist/src/components/accordion/AccordionContent.astro +13 -13
  5. package/dist/src/components/accordion/AccordionItem.astro +6 -6
  6. package/dist/src/components/accordion/AccordionTrigger.astro +13 -13
  7. package/dist/src/components/accordion/index.ts +4 -4
  8. package/dist/src/components/alert/Alert.astro +15 -15
  9. package/dist/src/components/alert/AlertDescription.astro +1 -1
  10. package/dist/src/components/alert/AlertTitle.astro +2 -2
  11. package/dist/src/components/alert/index.ts +3 -3
  12. package/dist/src/components/avatar/Avatar.astro +16 -16
  13. package/dist/src/components/avatar/AvatarFallback.astro +3 -3
  14. package/dist/src/components/avatar/AvatarImage.astro +12 -12
  15. package/dist/src/components/avatar/index.ts +4 -4
  16. package/dist/src/components/badge/Badge.astro +33 -33
  17. package/dist/src/components/breadcrumb/Breadcrumb.astro +1 -1
  18. package/dist/src/components/breadcrumb/BreadcrumbEllipsis.astro +6 -6
  19. package/dist/src/components/breadcrumb/BreadcrumbItem.astro +1 -1
  20. package/dist/src/components/breadcrumb/BreadcrumbLink.astro +8 -8
  21. package/dist/src/components/breadcrumb/BreadcrumbList.astro +2 -2
  22. package/dist/src/components/breadcrumb/BreadcrumbPage.astro +6 -6
  23. package/dist/src/components/breadcrumb/BreadcrumbSeparator.astro +7 -7
  24. package/dist/src/components/breadcrumb/index.ts +14 -14
  25. package/dist/src/components/button/Button.astro +38 -38
  26. package/dist/src/components/card/Card.astro +1 -1
  27. package/dist/src/components/card/CardContent.astro +1 -1
  28. package/dist/src/components/card/CardDescription.astro +1 -1
  29. package/dist/src/components/card/CardFooter.astro +1 -1
  30. package/dist/src/components/card/CardHeader.astro +1 -1
  31. package/dist/src/components/card/CardTitle.astro +1 -1
  32. package/dist/src/components/card/index.ts +7 -7
  33. package/dist/src/components/checkbox/Checkbox.astro +89 -89
  34. package/dist/src/components/dialog/Dialog.astro +237 -178
  35. package/dist/src/components/dialog/DialogClose.astro +14 -14
  36. package/dist/src/components/dialog/DialogContent.astro +32 -32
  37. package/dist/src/components/dialog/DialogDescription.astro +1 -1
  38. package/dist/src/components/dialog/DialogFooter.astro +1 -1
  39. package/dist/src/components/dialog/DialogHeader.astro +1 -1
  40. package/dist/src/components/dialog/DialogTitle.astro +6 -6
  41. package/dist/src/components/dialog/DialogTrigger.astro +26 -20
  42. package/dist/src/components/dialog/index.ts +16 -16
  43. package/dist/src/components/dropdown/Dropdown.astro +359 -359
  44. package/dist/src/components/dropdown/DropdownContent.astro +63 -63
  45. package/dist/src/components/dropdown/DropdownItem.astro +31 -31
  46. package/dist/src/components/dropdown/DropdownLabel.astro +14 -14
  47. package/dist/src/components/dropdown/DropdownSeparator.astro +5 -5
  48. package/dist/src/components/dropdown/DropdownTrigger.astro +26 -26
  49. package/dist/src/components/dropdown/index.ts +12 -12
  50. package/dist/src/components/dropzone/Dropzone.astro +232 -0
  51. package/dist/src/components/dropzone/DropzoneFilesList.astro +25 -0
  52. package/dist/src/components/dropzone/DropzoneLoadingIndicator.astro +10 -0
  53. package/dist/src/components/dropzone/DropzoneUploadIndicator.astro +10 -0
  54. package/dist/src/components/dropzone/index.ts +13 -0
  55. package/dist/src/components/input/Input.astro +12 -12
  56. package/dist/src/components/label/Label.astro +8 -8
  57. package/dist/src/components/pagination/Pagination.astro +1 -1
  58. package/dist/src/components/pagination/PaginationContent.astro +3 -3
  59. package/dist/src/components/pagination/PaginationEllipsis.astro +2 -2
  60. package/dist/src/components/pagination/PaginationItem.astro +3 -3
  61. package/dist/src/components/pagination/PaginationLink.astro +27 -27
  62. package/dist/src/components/pagination/PaginationNext.astro +7 -6
  63. package/dist/src/components/pagination/PaginationPrevious.astro +7 -6
  64. package/dist/src/components/pagination/index.ts +14 -14
  65. package/dist/src/components/progress/Progress.astro +151 -0
  66. package/dist/src/components/progress/index.ts +5 -0
  67. package/dist/src/components/radio-group/RadioGroup.astro +156 -0
  68. package/dist/src/components/radio-group/RadioGroupItem.astro +125 -0
  69. package/dist/src/components/radio-group/RadioGroupTypes.ts +6 -0
  70. package/dist/src/components/radio-group/index.ts +10 -0
  71. package/dist/src/components/select/Select.astro +515 -475
  72. package/dist/src/components/select/SelectContent.astro +62 -62
  73. package/dist/src/components/select/SelectItem.astro +27 -27
  74. package/dist/src/components/select/SelectLabel.astro +1 -1
  75. package/dist/src/components/select/SelectTrigger.astro +28 -28
  76. package/dist/src/components/select/SelectTypes.ts +11 -5
  77. package/dist/src/components/select/SelectValue.astro +5 -5
  78. package/dist/src/components/select/index.ts +16 -16
  79. package/dist/src/components/skeleton/Skeleton.astro +14 -0
  80. package/dist/src/components/skeleton/index.ts +5 -0
  81. package/dist/src/components/switch/Switch.astro +150 -150
  82. package/dist/src/components/switch/SwitchTypes.ts +4 -4
  83. package/dist/src/components/table/Table.astro +5 -5
  84. package/dist/src/components/table/TableBody.astro +3 -3
  85. package/dist/src/components/table/TableCaption.astro +3 -3
  86. package/dist/src/components/table/TableCell.astro +3 -3
  87. package/dist/src/components/table/TableFoot.astro +3 -3
  88. package/dist/src/components/table/TableHead.astro +3 -3
  89. package/dist/src/components/table/TableHeader.astro +3 -3
  90. package/dist/src/components/table/TableRow.astro +3 -3
  91. package/dist/src/components/table/index.ts +8 -8
  92. package/dist/src/components/tabs/Tabs.astro +250 -250
  93. package/dist/src/components/tabs/TabsContent.astro +10 -10
  94. package/dist/src/components/tabs/TabsList.astro +2 -2
  95. package/dist/src/components/tabs/TabsTrigger.astro +15 -15
  96. package/dist/src/components/tabs/index.ts +4 -4
  97. package/dist/src/components/textarea/Textarea.astro +16 -16
  98. package/dist/src/components/tooltip/Tooltip.astro +217 -217
  99. package/dist/src/components/tooltip/TooltipContent.astro +81 -81
  100. package/dist/src/components/tooltip/index.ts +3 -3
  101. package/package.json +6 -6
@@ -1,25 +1,25 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from "astro/types";
3
- import { type VariantProps, tv } from "tailwind-variants";
3
+ import { tv, type VariantProps } from "tailwind-variants";
4
4
 
5
5
  type Props = HTMLAttributes<"textarea"> & VariantProps<typeof textarea>;
6
6
 
7
7
  const textarea = tv({
8
- base: [
9
- "border-input bg-background text-foreground ring-offset-background min-h-10 w-full rounded-md border",
10
- "focus:outline-outline focus:ring-0 focus:outline-2 focus:outline-offset-2",
11
- "file:text-foreground file:border-0 file:bg-transparent file:text-sm file:font-medium",
12
- "disabled:cursor-not-allowed disabled:opacity-50",
13
- "peer placeholder:text-muted-foreground",
14
- ],
15
- variants: {
16
- size: {
17
- sm: "min-h-9 px-2 py-1 text-sm",
18
- md: "min-h-10 px-3 py-2 text-base",
19
- lg: "min-h-12 px-4 py-3 text-lg",
20
- },
21
- },
22
- defaultVariants: { size: "md" },
8
+ base: [
9
+ "border-input bg-background text-foreground ring-offset-background min-h-10 w-full rounded-md border",
10
+ "focus:outline-outline focus:ring-0 focus:outline-2 focus:outline-offset-2",
11
+ "file:text-foreground file:border-0 file:bg-transparent file:text-sm file:font-medium",
12
+ "disabled:cursor-not-allowed disabled:opacity-50",
13
+ "peer placeholder:text-muted-foreground",
14
+ ],
15
+ variants: {
16
+ size: {
17
+ sm: "min-h-9 px-2 py-1 text-sm",
18
+ md: "min-h-10 px-3 py-2 text-base",
19
+ lg: "min-h-12 px-4 py-3 text-lg",
20
+ },
21
+ },
22
+ defaultVariants: { size: "md" },
23
23
  });
24
24
 
25
25
  const { size, class: className, ...rest } = Astro.props;
@@ -3,234 +3,234 @@ import type { HTMLAttributes } from "astro/types";
3
3
  import { tv } from "tailwind-variants";
4
4
 
5
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;
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
18
  };
19
19
 
20
20
  const tooltip = tv({ base: "starwind-tooltip relative inline-block" });
21
21
 
22
22
  const {
23
- openDelay = 200,
24
- closeDelay = 200,
25
- disableHoverableContent = false,
26
- class: className,
23
+ openDelay = 200,
24
+ closeDelay = 200,
25
+ disableHoverableContent = false,
26
+ class: className,
27
27
  } = Astro.props;
28
28
  ---
29
29
 
30
30
  <div
31
- class={tooltip({ class: className })}
32
- data-state="closed"
33
- data-open-delay={openDelay}
34
- data-close-delay={closeDelay}
35
- {...!disableHoverableContent && { "data-content-hoverable": "" }}
31
+ class={tooltip({ class: className })}
32
+ data-state="closed"
33
+ data-open-delay={openDelay}
34
+ data-close-delay={closeDelay}
35
+ {...!disableHoverableContent && { "data-content-hoverable": "" }}
36
36
  >
37
- <slot />
37
+ <slot />
38
38
  </div>
39
39
 
40
40
  <script>
41
- class TooltipHandler {
42
- private tooltip: HTMLElement;
43
- private trigger: HTMLElement | null;
44
- private content: HTMLElement | null;
45
- private openTimerRef: number | null = null;
46
- private closeTimerRef: number | null = null;
47
- private contentId: string;
48
- private animationDuration = 150;
49
-
50
- constructor(tooltip: HTMLElement, idx: number) {
51
- this.contentId = `starwind-tooltip${idx}`;
52
- this.tooltip = tooltip;
53
- this.content = tooltip.querySelector(".starwind-tooltip-content");
54
-
55
- // if tooltip.firstElementChild is this.content, then get the next element
56
- this.trigger = tooltip.firstElementChild as HTMLElement;
57
- if (this.trigger === this.content) {
58
- this.trigger = this.trigger.nextElementSibling as HTMLElement;
59
- }
60
-
61
- this.trigger.classList.add("starwind-tooltip-trigger");
62
-
63
- if (!this.trigger || !this.content) return;
64
-
65
- // animationDuration is set with inline styles through passed prop to TooltipContent
66
- const animationDurationString = this.content.style.animationDuration;
67
- if (animationDurationString.endsWith("ms")) {
68
- this.animationDuration = parseFloat(animationDurationString);
69
- } else if (animationDurationString.endsWith("s")) {
70
- // using something like @playform/compress might optimize to use "s" instead of "ms"
71
- this.animationDuration = parseFloat(animationDurationString) * 1000;
72
- }
73
- this.init();
74
- }
75
-
76
- private init() {
77
- this.setupAccessibility();
78
- this.setupEvents();
79
- }
80
-
81
- private setupAccessibility() {
82
- if (!this.trigger || !this.content) return;
83
- this.trigger.setAttribute("aria-describedby", this.contentId);
84
- this.content.id = this.contentId;
85
- }
86
-
87
- private setupEvents() {
88
- if (!this.trigger || !this.content) return;
89
-
90
- // Trigger events
91
- this.trigger.addEventListener("mouseenter", () => this.show());
92
- this.trigger.addEventListener("mouseleave", () => this.hide());
93
- this.trigger.addEventListener("focus", () => this.show(true));
94
- this.trigger.addEventListener("blur", () => this.hide(true));
95
-
96
- // Content events
97
- if (this.tooltip.hasAttribute("data-content-hoverable")) {
98
- this.content.addEventListener("mouseenter", () => this.show());
99
- this.content.addEventListener("mouseleave", () => this.hide());
100
- }
101
-
102
- // if data-avoid-collisions exists, add resize listener to reset any translations
103
- if (this.content.hasAttribute("data-avoid-collisions")) {
104
- window.addEventListener(
105
- "resize",
106
- () => {
107
- if (!this.content) return;
108
- this.content.style.transform = "";
109
- },
110
- { passive: true },
111
- );
112
- }
113
-
114
- // Document events
115
- document.addEventListener("keydown", (e) => {
116
- if (e.key === "Escape" && this.tooltip.getAttribute("data-state") === "open") {
117
- this.hide(true);
118
- }
119
- });
120
-
121
- document.addEventListener("click", (e) => {
122
- if (
123
- !this.tooltip.contains(e.target as Node) &&
124
- this.tooltip.getAttribute("data-state") === "open"
125
- ) {
126
- this.hide(true);
127
- }
128
- });
129
- }
130
-
131
- private show(immediate: boolean = false) {
132
- if (!this.content || !this.trigger) return;
133
- if (immediate) {
134
- this.tooltip.setAttribute("data-state", "open");
135
- this.content.setAttribute("data-state", "open");
136
- this.content.style.display = "block";
137
- this.checkBoundary(this.content);
138
- this.clearOpenTimer();
139
- return;
140
- }
141
-
142
- this.clearCloseTimer();
143
-
144
- const delay = parseInt(this.tooltip.getAttribute("data-open-delay") || "700");
145
- this.openTimerRef = window.setTimeout(() => {
146
- if (!this.content || !this.trigger) return;
147
- this.tooltip.setAttribute("data-state", "open");
148
- this.content.setAttribute("data-state", "open");
149
- this.content.style.display = "block";
150
- this.checkBoundary(this.content);
151
- this.openTimerRef = null;
152
- }, delay);
153
- }
154
-
155
- private hide(immediate: boolean = false) {
156
- if (!this.content || !this.trigger) return;
157
- this.clearOpenTimer();
158
-
159
- if (immediate) {
160
- this.clearCloseTimer();
161
- this.tooltip.setAttribute("data-state", "closed");
162
- setTimeout(() => {
163
- if (!this.content) return;
164
- this.content.style.display = "none";
165
- }, this.animationDuration);
166
- this.content.setAttribute("data-state", "closed");
167
- return;
168
- }
169
-
170
- this.closeTimerRef = window.setTimeout(
171
- () => {
172
- this.tooltip.setAttribute("data-state", "closed");
173
- setTimeout(() => {
174
- if (!this.content) return;
175
- this.content.style.display = "none";
176
- }, this.animationDuration);
177
- if (!this.content) return;
178
- this.content.setAttribute("data-state", "closed");
179
- this.closeTimerRef = null;
180
- },
181
- parseInt(this.tooltip.getAttribute("data-close-delay") || "300"),
182
- );
183
- }
184
-
185
- private checkBoundary(tooltipElement: HTMLElement) {
186
- if (!tooltipElement) return;
187
-
188
- // if data-avoid-collisions does not exist, return
189
- if (!tooltipElement.hasAttribute("data-avoid-collisions")) return;
190
-
191
- const viewportWidth = window.innerWidth;
192
- const tooltipRect = tooltipElement.getBoundingClientRect();
193
- const padding = 16; // Add some padding from viewport edges
194
-
195
- // Check if tooltip extends beyond right edge of viewport
196
- if (tooltipRect.right > viewportWidth - padding) {
197
- const overflow = tooltipRect.right - (viewportWidth - padding);
198
- tooltipElement.style.transform = `translateX(-${overflow + padding}px)`;
199
- }
200
-
201
- // Check if tooltip extends beyond left edge of viewport
202
- if (tooltipRect.left < padding) {
203
- const overflow = padding - tooltipRect.left;
204
- tooltipElement.style.transform = `translateX(${overflow + padding}px)`;
205
- }
206
- }
207
-
208
- private clearOpenTimer() {
209
- if (this.openTimerRef) {
210
- window.clearTimeout(this.openTimerRef);
211
- this.openTimerRef = null;
212
- }
213
- }
214
-
215
- private clearCloseTimer() {
216
- if (this.closeTimerRef) {
217
- window.clearTimeout(this.closeTimerRef);
218
- this.closeTimerRef = null;
219
- }
220
- }
221
- }
222
-
223
- // Store instances in a WeakMap to avoid memory leaks
224
- const tooltipInstances = new WeakMap<HTMLElement, TooltipHandler>();
225
-
226
- const setupTooltips = () => {
227
- document.querySelectorAll<HTMLElement>(".starwind-tooltip").forEach((tooltip, idx) => {
228
- if (!tooltipInstances.has(tooltip)) {
229
- tooltipInstances.set(tooltip, new TooltipHandler(tooltip, idx));
230
- }
231
- });
232
- };
233
-
234
- setupTooltips();
235
- document.addEventListener("astro:after-swap", setupTooltips);
41
+ class TooltipHandler {
42
+ private tooltip: HTMLElement;
43
+ private trigger: HTMLElement | null;
44
+ private content: HTMLElement | null;
45
+ private openTimerRef: number | null = null;
46
+ private closeTimerRef: number | null = null;
47
+ private contentId: string;
48
+ private animationDuration = 150;
49
+
50
+ constructor(tooltip: HTMLElement, idx: number) {
51
+ this.contentId = `starwind-tooltip${idx}`;
52
+ this.tooltip = tooltip;
53
+ this.content = tooltip.querySelector(".starwind-tooltip-content");
54
+
55
+ // if tooltip.firstElementChild is this.content, then get the next element
56
+ this.trigger = tooltip.firstElementChild as HTMLElement;
57
+ if (this.trigger === this.content) {
58
+ this.trigger = this.trigger.nextElementSibling as HTMLElement;
59
+ }
60
+
61
+ this.trigger.classList.add("starwind-tooltip-trigger");
62
+
63
+ if (!this.trigger || !this.content) return;
64
+
65
+ // animationDuration is set with inline styles through passed prop to TooltipContent
66
+ const animationDurationString = this.content.style.animationDuration;
67
+ if (animationDurationString.endsWith("ms")) {
68
+ this.animationDuration = parseFloat(animationDurationString);
69
+ } else if (animationDurationString.endsWith("s")) {
70
+ // using something like @playform/compress might optimize to use "s" instead of "ms"
71
+ this.animationDuration = parseFloat(animationDurationString) * 1000;
72
+ }
73
+ this.init();
74
+ }
75
+
76
+ private init() {
77
+ this.setupAccessibility();
78
+ this.setupEvents();
79
+ }
80
+
81
+ private setupAccessibility() {
82
+ if (!this.trigger || !this.content) return;
83
+ this.trigger.setAttribute("aria-describedby", this.contentId);
84
+ this.content.id = this.contentId;
85
+ }
86
+
87
+ private setupEvents() {
88
+ if (!this.trigger || !this.content) return;
89
+
90
+ // Trigger events
91
+ this.trigger.addEventListener("mouseenter", () => this.show());
92
+ this.trigger.addEventListener("mouseleave", () => this.hide());
93
+ this.trigger.addEventListener("focus", () => this.show(true));
94
+ this.trigger.addEventListener("blur", () => this.hide(true));
95
+
96
+ // Content events
97
+ if (this.tooltip.hasAttribute("data-content-hoverable")) {
98
+ this.content.addEventListener("mouseenter", () => this.show());
99
+ this.content.addEventListener("mouseleave", () => this.hide());
100
+ }
101
+
102
+ // if data-avoid-collisions exists, add resize listener to reset any translations
103
+ if (this.content.hasAttribute("data-avoid-collisions")) {
104
+ window.addEventListener(
105
+ "resize",
106
+ () => {
107
+ if (!this.content) return;
108
+ this.content.style.transform = "";
109
+ },
110
+ { passive: true },
111
+ );
112
+ }
113
+
114
+ // Document events
115
+ document.addEventListener("keydown", (e) => {
116
+ if (e.key === "Escape" && this.tooltip.getAttribute("data-state") === "open") {
117
+ this.hide(true);
118
+ }
119
+ });
120
+
121
+ document.addEventListener("click", (e) => {
122
+ if (
123
+ !this.tooltip.contains(e.target as Node) &&
124
+ this.tooltip.getAttribute("data-state") === "open"
125
+ ) {
126
+ this.hide(true);
127
+ }
128
+ });
129
+ }
130
+
131
+ private show(immediate: boolean = false) {
132
+ if (!this.content || !this.trigger) return;
133
+ if (immediate) {
134
+ this.tooltip.setAttribute("data-state", "open");
135
+ this.content.setAttribute("data-state", "open");
136
+ this.content.style.display = "block";
137
+ this.checkBoundary(this.content);
138
+ this.clearOpenTimer();
139
+ return;
140
+ }
141
+
142
+ this.clearCloseTimer();
143
+
144
+ const delay = parseInt(this.tooltip.getAttribute("data-open-delay") || "700");
145
+ this.openTimerRef = window.setTimeout(() => {
146
+ if (!this.content || !this.trigger) return;
147
+ this.tooltip.setAttribute("data-state", "open");
148
+ this.content.setAttribute("data-state", "open");
149
+ this.content.style.display = "block";
150
+ this.checkBoundary(this.content);
151
+ this.openTimerRef = null;
152
+ }, delay);
153
+ }
154
+
155
+ private hide(immediate: boolean = false) {
156
+ if (!this.content || !this.trigger) return;
157
+ this.clearOpenTimer();
158
+
159
+ if (immediate) {
160
+ this.clearCloseTimer();
161
+ this.tooltip.setAttribute("data-state", "closed");
162
+ setTimeout(() => {
163
+ if (!this.content) return;
164
+ this.content.style.display = "none";
165
+ }, this.animationDuration);
166
+ this.content.setAttribute("data-state", "closed");
167
+ return;
168
+ }
169
+
170
+ this.closeTimerRef = window.setTimeout(
171
+ () => {
172
+ this.tooltip.setAttribute("data-state", "closed");
173
+ setTimeout(() => {
174
+ if (!this.content) return;
175
+ this.content.style.display = "none";
176
+ }, this.animationDuration);
177
+ if (!this.content) return;
178
+ this.content.setAttribute("data-state", "closed");
179
+ this.closeTimerRef = null;
180
+ },
181
+ parseInt(this.tooltip.getAttribute("data-close-delay") || "300"),
182
+ );
183
+ }
184
+
185
+ private checkBoundary(tooltipElement: HTMLElement) {
186
+ if (!tooltipElement) return;
187
+
188
+ // if data-avoid-collisions does not exist, return
189
+ if (!tooltipElement.hasAttribute("data-avoid-collisions")) return;
190
+
191
+ const viewportWidth = window.innerWidth;
192
+ const tooltipRect = tooltipElement.getBoundingClientRect();
193
+ const padding = 16; // Add some padding from viewport edges
194
+
195
+ // Check if tooltip extends beyond right edge of viewport
196
+ if (tooltipRect.right > viewportWidth - padding) {
197
+ const overflow = tooltipRect.right - (viewportWidth - padding);
198
+ tooltipElement.style.transform = `translateX(-${overflow + padding}px)`;
199
+ }
200
+
201
+ // Check if tooltip extends beyond left edge of viewport
202
+ if (tooltipRect.left < padding) {
203
+ const overflow = padding - tooltipRect.left;
204
+ tooltipElement.style.transform = `translateX(${overflow + padding}px)`;
205
+ }
206
+ }
207
+
208
+ private clearOpenTimer() {
209
+ if (this.openTimerRef) {
210
+ window.clearTimeout(this.openTimerRef);
211
+ this.openTimerRef = null;
212
+ }
213
+ }
214
+
215
+ private clearCloseTimer() {
216
+ if (this.closeTimerRef) {
217
+ window.clearTimeout(this.closeTimerRef);
218
+ this.closeTimerRef = null;
219
+ }
220
+ }
221
+ }
222
+
223
+ // Store instances in a WeakMap to avoid memory leaks
224
+ const tooltipInstances = new WeakMap<HTMLElement, TooltipHandler>();
225
+
226
+ const setupTooltips = () => {
227
+ document.querySelectorAll<HTMLElement>(".starwind-tooltip").forEach((tooltip, idx) => {
228
+ if (!tooltipInstances.has(tooltip)) {
229
+ tooltipInstances.set(tooltip, new TooltipHandler(tooltip, idx));
230
+ }
231
+ });
232
+ };
233
+
234
+ setupTooltips();
235
+ document.addEventListener("astro:after-swap", setupTooltips);
236
236
  </script>