@starwind-ui/core 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/index.js +86 -2
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/components/accordion/Accordion.astro +248 -0
  4. package/dist/src/components/accordion/AccordionContent.astro +28 -0
  5. package/dist/src/components/accordion/AccordionItem.astro +25 -0
  6. package/dist/src/components/accordion/AccordionTrigger.astro +26 -0
  7. package/dist/src/components/accordion/index.ts +13 -0
  8. package/dist/src/components/alert/Alert.astro +44 -0
  9. package/dist/src/components/alert/AlertDescription.astro +11 -0
  10. package/dist/src/components/alert/AlertTitle.astro +17 -0
  11. package/dist/src/components/alert/index.ts +11 -0
  12. package/dist/src/components/avatar/Avatar.astro +44 -0
  13. package/dist/src/components/avatar/AvatarFallback.astro +16 -0
  14. package/dist/src/components/avatar/AvatarImage.astro +48 -0
  15. package/dist/src/components/avatar/index.ts +11 -0
  16. package/dist/src/components/card/Card.astro +14 -0
  17. package/dist/src/components/card/CardContent.astro +11 -0
  18. package/dist/src/components/card/CardDescription.astro +11 -0
  19. package/dist/src/components/card/CardFooter.astro +11 -0
  20. package/dist/src/components/card/CardHeader.astro +11 -0
  21. package/dist/src/components/card/CardTitle.astro +11 -0
  22. package/dist/src/components/card/index.ts +17 -0
  23. package/dist/src/components/checkbox/Checkbox.astro +105 -0
  24. package/dist/src/components/checkbox/index.ts +5 -0
  25. package/dist/src/components/dialog/Dialog.astro +175 -0
  26. package/dist/src/components/dialog/DialogClose.astro +30 -0
  27. package/dist/src/components/dialog/DialogContent.astro +57 -0
  28. package/dist/src/components/dialog/DialogDescription.astro +11 -0
  29. package/dist/src/components/dialog/DialogFooter.astro +11 -0
  30. package/dist/src/components/dialog/DialogHeader.astro +11 -0
  31. package/dist/src/components/dialog/DialogTitle.astro +16 -0
  32. package/dist/src/components/dialog/DialogTrigger.astro +35 -0
  33. package/dist/src/components/dialog/index.ts +30 -0
  34. package/dist/src/components/input/Input.astro +26 -0
  35. package/dist/src/components/input/index.ts +5 -0
  36. package/dist/src/components/label/Label.astro +25 -0
  37. package/dist/src/components/label/index.ts +5 -0
  38. package/dist/src/components/pagination/Pagination.astro +18 -0
  39. package/dist/src/components/pagination/PaginationContent.astro +13 -0
  40. package/dist/src/components/pagination/PaginationEllipsis.astro +15 -0
  41. package/dist/src/components/pagination/PaginationItem.astro +13 -0
  42. package/dist/src/components/pagination/PaginationLink.astro +50 -0
  43. package/dist/src/components/pagination/PaginationNext.astro +23 -0
  44. package/dist/src/components/pagination/PaginationPrevious.astro +23 -0
  45. package/dist/src/components/pagination/index.ts +27 -0
  46. package/dist/src/components/select/Select.astro +452 -0
  47. package/dist/src/components/select/SelectContent.astro +57 -0
  48. package/dist/src/components/select/SelectGroup.astro +10 -0
  49. package/dist/src/components/select/SelectItem.astro +41 -0
  50. package/dist/src/components/select/SelectLabel.astro +11 -0
  51. package/dist/src/components/select/SelectSeparator.astro +9 -0
  52. package/dist/src/components/select/SelectTrigger.astro +40 -0
  53. package/dist/src/components/select/SelectTypes.ts +7 -0
  54. package/dist/src/components/select/SelectValue.astro +16 -0
  55. package/dist/src/components/select/index.ts +30 -0
  56. package/dist/src/components/switch/Switch.astro +189 -0
  57. package/dist/src/components/switch/SwitchTypes.ts +6 -0
  58. package/dist/src/components/switch/index.ts +6 -0
  59. package/dist/src/components/tabs/Tabs.astro +246 -0
  60. package/dist/src/components/tabs/TabsContent.astro +22 -0
  61. package/dist/src/components/tabs/TabsList.astro +19 -0
  62. package/dist/src/components/tabs/TabsTrigger.astro +27 -0
  63. package/dist/src/components/tabs/index.ts +13 -0
  64. package/dist/src/components/textarea/Textarea.astro +25 -0
  65. package/dist/src/components/textarea/index.ts +5 -0
  66. package/dist/src/components/tooltip/Tooltip.astro +233 -0
  67. package/dist/src/components/tooltip/TooltipContent.astro +76 -0
  68. package/dist/src/components/tooltip/TooltipTrigger.astro +11 -0
  69. package/dist/src/components/tooltip/index.ts +11 -0
  70. package/package.json +1 -1
@@ -0,0 +1,25 @@
1
+ ---
2
+ import type { HTMLAttributes } from "astro/types";
3
+
4
+ type Props = HTMLAttributes<"textarea"> & {
5
+ size?: "sm" | "md" | "lg";
6
+ };
7
+
8
+ const { size = "md", class: className, ...rest } = Astro.props;
9
+ ---
10
+
11
+ <textarea
12
+ class:list={[
13
+ "border-input bg-background text-foreground ring-offset-background min-h-10 w-full rounded-md border",
14
+ "focus:outline-outline focus:ring-0 focus:outline-2 focus:outline-offset-2",
15
+ "file:text-foreground file:border-0 file:bg-transparent file:text-sm file:font-medium",
16
+ "disabled:cursor-not-allowed disabled:opacity-50",
17
+ "peer placeholder:text-muted-foreground",
18
+ {
19
+ "min-h-9 px-2 py-1 text-sm": size === "sm",
20
+ "min-h-10 px-3 py-2 text-base": size === "md",
21
+ "min-h-12 px-4 py-3 text-lg": size === "lg",
22
+ },
23
+ className,
24
+ ]}
25
+ {...rest}></textarea>
@@ -0,0 +1,5 @@
1
+ import Textarea from "./Textarea.astro";
2
+
3
+ export { Textarea };
4
+
5
+ export default Textarea;
@@ -0,0 +1,233 @@
1
+ ---
2
+ import type { HTMLAttributes } from "astro/types";
3
+
4
+ type Props = HTMLAttributes<"div"> & {
5
+ /**
6
+ * Time in milliseconds to wait before showing the tooltip
7
+ */
8
+ openDelay?: number;
9
+ /**
10
+ * Time in milliseconds to wait before hiding the tooltip
11
+ */
12
+ closeDelay?: number;
13
+ /**
14
+ * When true, prevents the tooltip from staying open when hovering over its content
15
+ */
16
+ disableHoverableContent?: boolean;
17
+ };
18
+
19
+ const {
20
+ openDelay = 200,
21
+ closeDelay = 200,
22
+ disableHoverableContent = false,
23
+ class: className,
24
+ } = Astro.props;
25
+ ---
26
+
27
+ <div
28
+ class:list={["starwind-tooltip relative inline-block", className]}
29
+ data-state="closed"
30
+ data-open-delay={openDelay}
31
+ data-close-delay={closeDelay}
32
+ {...!disableHoverableContent && { "data-content-hoverable": "" }}
33
+ >
34
+ <slot />
35
+ </div>
36
+
37
+ <script>
38
+ class TooltipHandler {
39
+ private tooltip: HTMLElement;
40
+ private trigger: HTMLElement | null;
41
+ private content: HTMLElement | null;
42
+ private openTimerRef: number | null = null;
43
+ private closeTimerRef: number | null = null;
44
+ private contentId: string;
45
+ private animationDuration = 150;
46
+
47
+ constructor(tooltip: HTMLElement, idx: number) {
48
+ this.contentId = `starwind-tooltip${idx}`;
49
+ this.tooltip = tooltip;
50
+ this.content = tooltip.querySelector(".starwind-tooltip-content");
51
+
52
+ // if tooltip.firstElementChild is this.content, then get the next element
53
+ this.trigger = tooltip.firstElementChild as HTMLElement;
54
+ if (this.trigger === this.content) {
55
+ this.trigger = this.trigger.nextElementSibling as HTMLElement;
56
+ }
57
+
58
+ this.trigger.classList.add("starwind-tooltip-trigger");
59
+
60
+ if (!this.trigger || !this.content) return;
61
+
62
+ // animationDuration is set with inline styles through passed prop to TooltipContent
63
+ const animationDurationString = this.content.style.animationDuration;
64
+ if (animationDurationString.endsWith("ms")) {
65
+ this.animationDuration = parseFloat(animationDurationString);
66
+ } else if (animationDurationString.endsWith("s")) {
67
+ // using something like @playform/compress might optimize to use "s" instead of "ms"
68
+ this.animationDuration = parseFloat(animationDurationString) * 1000;
69
+ }
70
+ this.init();
71
+ }
72
+
73
+ private init() {
74
+ this.setupAccessibility();
75
+ this.setupEvents();
76
+ }
77
+
78
+ private setupAccessibility() {
79
+ if (!this.trigger || !this.content) return;
80
+ this.trigger.setAttribute("aria-describedby", this.contentId);
81
+ this.content.id = this.contentId;
82
+ }
83
+
84
+ private setupEvents() {
85
+ if (!this.trigger || !this.content) return;
86
+
87
+ // Trigger events
88
+ this.trigger.addEventListener("mouseenter", () => this.show());
89
+ this.trigger.addEventListener("mouseleave", () => this.hide());
90
+ this.trigger.addEventListener("focus", () => this.show(true));
91
+ this.trigger.addEventListener("blur", () => this.hide(true));
92
+
93
+ // Content events
94
+ if (this.tooltip.hasAttribute("data-content-hoverable")) {
95
+ this.content.addEventListener("mouseenter", () => this.show());
96
+ this.content.addEventListener("mouseleave", () => this.hide());
97
+ }
98
+
99
+ // if data-avoid-collisions exists, add resize listener to reset any translations
100
+ if (this.content.hasAttribute("data-avoid-collisions")) {
101
+ window.addEventListener(
102
+ "resize",
103
+ () => {
104
+ if (!this.content) return;
105
+ this.content.style.transform = "";
106
+ },
107
+ { passive: true },
108
+ );
109
+ }
110
+
111
+ // Document events
112
+ document.addEventListener("keydown", (e) => {
113
+ if (e.key === "Escape" && this.tooltip.getAttribute("data-state") === "open") {
114
+ this.hide(true);
115
+ }
116
+ });
117
+
118
+ document.addEventListener("click", (e) => {
119
+ if (
120
+ !this.tooltip.contains(e.target as Node) &&
121
+ this.tooltip.getAttribute("data-state") === "open"
122
+ ) {
123
+ this.hide(true);
124
+ }
125
+ });
126
+ }
127
+
128
+ private show(immediate: boolean = false) {
129
+ if (!this.content || !this.trigger) return;
130
+ if (immediate) {
131
+ this.tooltip.setAttribute("data-state", "open");
132
+ this.content.setAttribute("data-state", "open");
133
+ this.content.style.display = "block";
134
+ this.checkBoundary(this.content);
135
+ this.clearOpenTimer();
136
+ return;
137
+ }
138
+
139
+ this.clearCloseTimer();
140
+
141
+ const delay = parseInt(this.tooltip.getAttribute("data-open-delay") || "700");
142
+ this.openTimerRef = window.setTimeout(() => {
143
+ if (!this.content || !this.trigger) return;
144
+ this.tooltip.setAttribute("data-state", "open");
145
+ this.content.setAttribute("data-state", "open");
146
+ this.content.style.display = "block";
147
+ this.checkBoundary(this.content);
148
+ this.openTimerRef = null;
149
+ }, delay);
150
+ }
151
+
152
+ private hide(immediate: boolean = false) {
153
+ if (!this.content || !this.trigger) return;
154
+ this.clearOpenTimer();
155
+
156
+ if (immediate) {
157
+ this.clearCloseTimer();
158
+ this.tooltip.setAttribute("data-state", "closed");
159
+ setTimeout(() => {
160
+ if (!this.content) return;
161
+ this.content.style.display = "none";
162
+ }, this.animationDuration - 10);
163
+ this.content.setAttribute("data-state", "closed");
164
+ return;
165
+ }
166
+
167
+ this.closeTimerRef = window.setTimeout(
168
+ () => {
169
+ this.tooltip.setAttribute("data-state", "closed");
170
+ setTimeout(() => {
171
+ if (!this.content) return;
172
+ this.content.style.display = "none";
173
+ }, this.animationDuration - 10);
174
+ if (!this.content) return;
175
+ this.content.setAttribute("data-state", "closed");
176
+ this.closeTimerRef = null;
177
+ },
178
+ parseInt(this.tooltip.getAttribute("data-close-delay") || "300"),
179
+ );
180
+ }
181
+
182
+ private checkBoundary(tooltipElement: HTMLElement) {
183
+ if (!tooltipElement) return;
184
+
185
+ // if data-avoid-collisions does not exist, return
186
+ if (!tooltipElement.hasAttribute("data-avoid-collisions")) return;
187
+
188
+ const viewportWidth = window.innerWidth;
189
+ const tooltipRect = tooltipElement.getBoundingClientRect();
190
+ const padding = 16; // Add some padding from viewport edges
191
+
192
+ // Check if tooltip extends beyond right edge of viewport
193
+ if (tooltipRect.right > viewportWidth - padding) {
194
+ const overflow = tooltipRect.right - (viewportWidth - padding);
195
+ tooltipElement.style.transform = `translateX(-${overflow + padding}px)`;
196
+ }
197
+
198
+ // Check if tooltip extends beyond left edge of viewport
199
+ if (tooltipRect.left < padding) {
200
+ const overflow = padding - tooltipRect.left;
201
+ tooltipElement.style.transform = `translateX(${overflow + padding}px)`;
202
+ }
203
+ }
204
+
205
+ private clearOpenTimer() {
206
+ if (this.openTimerRef) {
207
+ window.clearTimeout(this.openTimerRef);
208
+ this.openTimerRef = null;
209
+ }
210
+ }
211
+
212
+ private clearCloseTimer() {
213
+ if (this.closeTimerRef) {
214
+ window.clearTimeout(this.closeTimerRef);
215
+ this.closeTimerRef = null;
216
+ }
217
+ }
218
+ }
219
+
220
+ // Store instances in a WeakMap to avoid memory leaks
221
+ const tooltipInstances = new WeakMap<HTMLElement, TooltipHandler>();
222
+
223
+ const setupTooltips = () => {
224
+ document.querySelectorAll<HTMLElement>(".starwind-tooltip").forEach((tooltip, idx) => {
225
+ if (!tooltipInstances.has(tooltip)) {
226
+ tooltipInstances.set(tooltip, new TooltipHandler(tooltip, idx));
227
+ }
228
+ });
229
+ };
230
+
231
+ setupTooltips();
232
+ document.addEventListener("astro:after-swap", setupTooltips);
233
+ </script>
@@ -0,0 +1,76 @@
1
+ ---
2
+ import type { HTMLAttributes } from "astro/types";
3
+
4
+ type Props = HTMLAttributes<"div"> & {
5
+ /**
6
+ * Side of the tooltip
7
+ * @default top
8
+ */
9
+ side?: "top" | "right" | "bottom" | "left";
10
+ /**
11
+ * Alignment of the tooltip
12
+ * @default center
13
+ */
14
+ align?: "start" | "center" | "end";
15
+ /**
16
+ * Offset distance in pixels
17
+ * @default 4
18
+ */
19
+ sideOffset?: number;
20
+ /**
21
+ * Prevent the tooltip from colliding with other elements
22
+ * @default true
23
+ */
24
+ avoidCollisions?: boolean;
25
+ /**
26
+ * Open and close animation duration in milliseconds
27
+ * @default 150
28
+ */
29
+ animationDuration?: number;
30
+ };
31
+
32
+ const {
33
+ side = "top",
34
+ align = "center",
35
+ sideOffset = 4,
36
+ avoidCollisions = true,
37
+ animationDuration = 150,
38
+ class: className,
39
+ } = Astro.props;
40
+ ---
41
+
42
+ <div
43
+ class:list={[
44
+ "starwind-tooltip-content",
45
+ "absolute z-50 hidden px-3 py-1.5 whitespace-nowrap shadow-sm",
46
+ "bg-popover text-popover-foreground rounded-md border",
47
+ "animate-in fade-in-0 zoom-in-95",
48
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
49
+ {
50
+ "slide-in-from-right-2 right-(--tooltip-offset)": side === "left",
51
+ "slide-in-from-left-2 left-(--tooltip-offset)": side === "right",
52
+ "slide-in-from-top-2 top-(--tooltip-offset)": side === "bottom",
53
+ "slide-in-from-bottom-2 bottom-(--tooltip-offset)": side === "top",
54
+ },
55
+ {
56
+ "left-[50%] translate-x-[-50%]": align === "center" && (side === "top" || side === "bottom"),
57
+ "top-[50%] translate-y-[-50%]": align === "center" && (side === "left" || side === "right"),
58
+ "left-0": align === "start" && (side === "top" || side === "bottom"),
59
+ "right-0": align === "end" && (side === "top" || side === "bottom"),
60
+ "top-0": align === "start" && (side === "left" || side === "right"),
61
+ "bottom-0": align === "end" && (side === "left" || side === "right"),
62
+ },
63
+ className,
64
+ ]}
65
+ data-state="closed"
66
+ data-side={side}
67
+ data-align={align}
68
+ {...avoidCollisions && { "data-avoid-collisions": "" }}
69
+ role="tooltip"
70
+ style={{
71
+ "--tooltip-offset": `calc(100% + ${sideOffset}px)`,
72
+ animationDuration: `${animationDuration}ms`,
73
+ }}
74
+ >
75
+ <slot> My tooltip! </slot>
76
+ </div>
@@ -0,0 +1,11 @@
1
+ ---
2
+ /**
3
+ * This component is used to trigger the tooltip
4
+ * All it does is require a slot
5
+ */
6
+ type Props = {
7
+ children: any;
8
+ };
9
+ ---
10
+
11
+ <slot />
@@ -0,0 +1,11 @@
1
+ import Tooltip from "./Tooltip.astro";
2
+ import TooltipContent from "./TooltipContent.astro";
3
+ import TooltipTrigger from "./TooltipTrigger.astro";
4
+
5
+ export { Tooltip, TooltipContent, TooltipTrigger };
6
+
7
+ export default {
8
+ Root: Tooltip,
9
+ Trigger: TooltipTrigger,
10
+ Content: TooltipContent,
11
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starwind-ui/core",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Starwind UI core components and registry",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",