@starwind-ui/core 1.13.0 → 1.15.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.
- package/dist/index.js +28 -5
- package/dist/index.js.map +1 -1
- package/dist/src/components/badge/Badge.astro +8 -2
- package/dist/src/components/button/Button.astro +8 -7
- package/dist/src/components/collapsible/Collapsible.astro +161 -0
- package/dist/src/components/collapsible/CollapsibleContent.astro +22 -0
- package/dist/src/components/collapsible/CollapsibleTrigger.astro +44 -0
- package/dist/src/components/collapsible/index.ts +13 -0
- package/dist/src/components/dialog/Dialog.astro +35 -1
- package/dist/src/components/input-otp/InputOtp.astro +319 -0
- package/dist/src/components/input-otp/InputOtpGroup.astro +16 -0
- package/dist/src/components/input-otp/InputOtpSeparator.astro +25 -0
- package/dist/src/components/input-otp/InputOtpSlot.astro +48 -0
- package/dist/src/components/input-otp/InputOtpTypes.ts +6 -0
- package/dist/src/components/input-otp/index.ts +33 -0
- package/dist/src/components/prose/Prose.astro +617 -0
- package/dist/src/components/prose/index.ts +9 -0
- package/dist/src/components/select/Select.astro +3 -0
- package/dist/src/components/sidebar/Sidebar.astro +213 -0
- package/dist/src/components/sidebar/SidebarContent.astro +24 -0
- package/dist/src/components/sidebar/SidebarFooter.astro +21 -0
- package/dist/src/components/sidebar/SidebarGroup.astro +21 -0
- package/dist/src/components/sidebar/SidebarGroupContent.astro +21 -0
- package/dist/src/components/sidebar/SidebarGroupLabel.astro +52 -0
- package/dist/src/components/sidebar/SidebarHeader.astro +21 -0
- package/dist/src/components/sidebar/SidebarInput.astro +22 -0
- package/dist/src/components/sidebar/SidebarInset.astro +21 -0
- package/dist/src/components/sidebar/SidebarMenu.astro +21 -0
- package/dist/src/components/sidebar/SidebarMenuAction.astro +59 -0
- package/dist/src/components/sidebar/SidebarMenuBadge.astro +30 -0
- package/dist/src/components/sidebar/SidebarMenuButton.astro +129 -0
- package/dist/src/components/sidebar/SidebarMenuItem.astro +21 -0
- package/dist/src/components/sidebar/SidebarMenuSkeleton.astro +40 -0
- package/dist/src/components/sidebar/SidebarMenuSub.astro +24 -0
- package/dist/src/components/sidebar/SidebarMenuSubButton.astro +49 -0
- package/dist/src/components/sidebar/SidebarMenuSubItem.astro +16 -0
- package/dist/src/components/sidebar/SidebarProvider.astro +213 -0
- package/dist/src/components/sidebar/SidebarRail.astro +71 -0
- package/dist/src/components/sidebar/SidebarSeparator.astro +22 -0
- package/dist/src/components/sidebar/SidebarTrigger.astro +66 -0
- package/dist/src/components/sidebar/index.ts +103 -0
- package/dist/src/components/theme-toggle/ThemeToggle.astro +208 -0
- package/dist/src/components/theme-toggle/index.ts +7 -0
- package/dist/src/components/toggle/Toggle.astro +1 -1
- package/dist/src/components/tooltip/Tooltip.astro +80 -37
- package/dist/src/components/tooltip/TooltipContent.astro +9 -34
- package/package.json +1 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
---
|
|
2
|
+
// put the following script in the <head> of each page of your site
|
|
3
|
+
// this ensures it is run early to eliminate any flashes of incorrect color theme
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
<script is:inline>
|
|
7
|
+
function initTheme() {
|
|
8
|
+
const colorTheme = localStorage.getItem("colorTheme");
|
|
9
|
+
if (!colorTheme) {
|
|
10
|
+
// if no color theme yet, use the users preferences
|
|
11
|
+
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
12
|
+
document.documentElement.classList.add("dark");
|
|
13
|
+
localStorage.setItem("colorTheme", "dark");
|
|
14
|
+
} else {
|
|
15
|
+
document.documentElement.classList.remove("dark");
|
|
16
|
+
localStorage.setItem("colorTheme", "light");
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
// assign the theme based on the value in local storage
|
|
20
|
+
if (colorTheme === "dark") {
|
|
21
|
+
document.documentElement.classList.add("dark");
|
|
22
|
+
} else if (colorTheme === "light") {
|
|
23
|
+
document.documentElement.classList.remove("dark");
|
|
24
|
+
} else if (colorTheme === "system") {
|
|
25
|
+
// use system preference
|
|
26
|
+
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
27
|
+
document.documentElement.classList.toggle("dark", prefersDark);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// runs on initial page load
|
|
33
|
+
initTheme();
|
|
34
|
+
|
|
35
|
+
// runs on view transitions navigation
|
|
36
|
+
document.addEventListener("astro:after-swap", initTheme);
|
|
37
|
+
</script>
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import Moon from "@tabler/icons/outline/moon.svg";
|
|
41
|
+
import Sun from "@tabler/icons/outline/sun.svg";
|
|
42
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
43
|
+
|
|
44
|
+
import { Toggle, ToggleVariants } from "@/components/starwind/toggle";
|
|
45
|
+
|
|
46
|
+
export const themeToggle = tv({
|
|
47
|
+
base: [
|
|
48
|
+
"starwind-theme-toggle",
|
|
49
|
+
"group hover:border-muted-foreground hover:bg-transparent data-[state=on]:bg-transparent",
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
type Props = VariantProps<typeof ToggleVariants.toggle> & {
|
|
54
|
+
/**
|
|
55
|
+
* The ARIA label for the toggle
|
|
56
|
+
*/
|
|
57
|
+
ariaLabel?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Additional classes for the toggle
|
|
60
|
+
*/
|
|
61
|
+
class?: string;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const {
|
|
65
|
+
class: className = "",
|
|
66
|
+
variant = "outline",
|
|
67
|
+
size = "md",
|
|
68
|
+
ariaLabel = "Toggle theme",
|
|
69
|
+
...rest
|
|
70
|
+
} = Astro.props as Props;
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
<Toggle
|
|
74
|
+
class={themeToggle({ class: className })}
|
|
75
|
+
variant={variant}
|
|
76
|
+
size={size}
|
|
77
|
+
aria-label={ariaLabel}
|
|
78
|
+
data-slot="theme-toggle"
|
|
79
|
+
{...rest}
|
|
80
|
+
>
|
|
81
|
+
<slot>
|
|
82
|
+
<span class="size-5" data-theme-icon-wrapper>
|
|
83
|
+
<slot name="light-icon">
|
|
84
|
+
<Sun class="hidden size-5 group-data-[state=off]:data-ready:block" data-theme-icon />
|
|
85
|
+
</slot>
|
|
86
|
+
<slot name="dark-icon">
|
|
87
|
+
<Moon class="hidden size-5 group-data-[state=on]:data-ready:block" data-theme-icon />
|
|
88
|
+
</slot>
|
|
89
|
+
</span>
|
|
90
|
+
</slot>
|
|
91
|
+
</Toggle>
|
|
92
|
+
|
|
93
|
+
<script>
|
|
94
|
+
import type { ToggleChangeEvent } from "@/components/starwind/toggle";
|
|
95
|
+
|
|
96
|
+
class ThemeToggleHandler {
|
|
97
|
+
private toggle: HTMLButtonElement;
|
|
98
|
+
private isInitializing = true;
|
|
99
|
+
|
|
100
|
+
constructor(toggle: HTMLButtonElement) {
|
|
101
|
+
this.toggle = toggle;
|
|
102
|
+
|
|
103
|
+
this.initializeState();
|
|
104
|
+
this.setupEventListeners();
|
|
105
|
+
|
|
106
|
+
// Mark initialization complete after a microtask
|
|
107
|
+
queueMicrotask(() => {
|
|
108
|
+
this.isInitializing = false;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private initializeState(): void {
|
|
113
|
+
// Determine if dark mode is active based on the document class
|
|
114
|
+
// This handles "system" correctly since initTheme already applied the right class
|
|
115
|
+
const isDark = document.documentElement.classList.contains("dark");
|
|
116
|
+
|
|
117
|
+
this.toggle.setAttribute("data-state", isDark ? "on" : "off");
|
|
118
|
+
this.toggle.setAttribute("aria-pressed", isDark.toString());
|
|
119
|
+
|
|
120
|
+
// Mark icons as ready to show (prevents flash of wrong icon)
|
|
121
|
+
this.toggle.querySelectorAll("[data-theme-icon]").forEach((icon) => {
|
|
122
|
+
icon.setAttribute("data-ready", "");
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private setupEventListeners(): void {
|
|
127
|
+
this.toggle.addEventListener("starwind-toggle:change", (event: Event) =>
|
|
128
|
+
this.handleToggleChange(event),
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private handleToggleChange(event: Event): void {
|
|
133
|
+
// Don't dispatch theme:change during initialization
|
|
134
|
+
if (this.isInitializing) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const { pressed } = (event as ToggleChangeEvent).detail;
|
|
139
|
+
|
|
140
|
+
// Update theme based on toggle state
|
|
141
|
+
const newTheme = pressed ? "dark" : "light";
|
|
142
|
+
|
|
143
|
+
document.documentElement.classList.toggle("dark", pressed);
|
|
144
|
+
localStorage.setItem("colorTheme", newTheme);
|
|
145
|
+
|
|
146
|
+
// Dispatch custom event to sync other theme toggles
|
|
147
|
+
document.dispatchEvent(
|
|
148
|
+
new CustomEvent("theme:change", {
|
|
149
|
+
detail: { theme: newTheme },
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public syncState(theme: string): void {
|
|
155
|
+
let shouldBeOn: boolean;
|
|
156
|
+
if (theme === "system") {
|
|
157
|
+
shouldBeOn = document.documentElement.classList.contains("dark");
|
|
158
|
+
} else {
|
|
159
|
+
shouldBeOn = theme === "dark";
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const newState = shouldBeOn ? "on" : "off";
|
|
163
|
+
|
|
164
|
+
// Only update if state actually changed to prevent loops
|
|
165
|
+
if (this.toggle.getAttribute("data-state") !== newState) {
|
|
166
|
+
this.toggle.setAttribute("data-state", newState);
|
|
167
|
+
this.toggle.setAttribute("aria-pressed", shouldBeOn.toString());
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Store instances in a WeakMap to avoid memory leaks
|
|
173
|
+
const themeToggleInstances = new WeakMap<HTMLElement, ThemeToggleHandler>();
|
|
174
|
+
// Track active toggles for iteration (WeakMap is not iterable)
|
|
175
|
+
const activeToggles = new Set<HTMLButtonElement>();
|
|
176
|
+
|
|
177
|
+
const setupThemeToggles = (clearExisting = false) => {
|
|
178
|
+
// Clear stale references on page transitions to prevent memory leaks
|
|
179
|
+
if (clearExisting) {
|
|
180
|
+
activeToggles.clear();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
document.querySelectorAll<HTMLButtonElement>(".starwind-theme-toggle").forEach((toggle) => {
|
|
184
|
+
if (!themeToggleInstances.has(toggle)) {
|
|
185
|
+
themeToggleInstances.set(toggle, new ThemeToggleHandler(toggle));
|
|
186
|
+
}
|
|
187
|
+
activeToggles.add(toggle);
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Listen for theme changes from other toggles
|
|
192
|
+
const handleThemeChange = (event: Event) => {
|
|
193
|
+
const { theme } = (event as CustomEvent).detail;
|
|
194
|
+
|
|
195
|
+
activeToggles.forEach((toggle) => {
|
|
196
|
+
const instance = themeToggleInstances.get(toggle);
|
|
197
|
+
if (instance) {
|
|
198
|
+
instance.syncState(theme);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
document.addEventListener("theme:change", handleThemeChange);
|
|
204
|
+
|
|
205
|
+
setupThemeToggles();
|
|
206
|
+
document.addEventListener("astro:after-swap", () => setupThemeToggles(true));
|
|
207
|
+
document.addEventListener("starwind:init", () => setupThemeToggles());
|
|
208
|
+
</script>
|
|
@@ -10,7 +10,7 @@ export const toggle = tv({
|
|
|
10
10
|
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
11
11
|
"focus-visible:border-outline focus-visible:ring-outline/50 focus-visible:ring-3",
|
|
12
12
|
"transition-colors outline-none",
|
|
13
|
-
"aria-invalid:ring-error/
|
|
13
|
+
"aria-invalid:ring-error/40 aria-invalid:border-error",
|
|
14
14
|
],
|
|
15
15
|
variants: {
|
|
16
16
|
variant: {
|
|
@@ -100,18 +100,6 @@ const {
|
|
|
100
100
|
this.content.addEventListener("mouseleave", () => this.hide());
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
// if data-avoid-collisions exists, add resize listener to reset any translations
|
|
104
|
-
if (this.content.hasAttribute("data-avoid-collisions")) {
|
|
105
|
-
window.addEventListener(
|
|
106
|
-
"resize",
|
|
107
|
-
() => {
|
|
108
|
-
if (!this.content) return;
|
|
109
|
-
this.content.style.transform = "";
|
|
110
|
-
},
|
|
111
|
-
{ passive: true },
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
103
|
// Document events
|
|
116
104
|
document.addEventListener("keydown", (e) => {
|
|
117
105
|
if (e.key === "Escape" && this.tooltip.getAttribute("data-state") === "open") {
|
|
@@ -135,7 +123,7 @@ const {
|
|
|
135
123
|
this.tooltip.setAttribute("data-state", "open");
|
|
136
124
|
this.content.setAttribute("data-state", "open");
|
|
137
125
|
this.content.style.display = "block";
|
|
138
|
-
this.
|
|
126
|
+
this.positionTooltip();
|
|
139
127
|
this.clearOpenTimer();
|
|
140
128
|
return;
|
|
141
129
|
}
|
|
@@ -148,11 +136,89 @@ const {
|
|
|
148
136
|
this.tooltip.setAttribute("data-state", "open");
|
|
149
137
|
this.content.setAttribute("data-state", "open");
|
|
150
138
|
this.content.style.display = "block";
|
|
151
|
-
this.
|
|
139
|
+
this.positionTooltip();
|
|
152
140
|
this.openTimerRef = null;
|
|
153
141
|
}, delay);
|
|
154
142
|
}
|
|
155
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
|
+
|
|
156
222
|
private hide(immediate: boolean = false) {
|
|
157
223
|
if (!this.content || !this.trigger) return;
|
|
158
224
|
this.clearOpenTimer();
|
|
@@ -183,29 +249,6 @@ const {
|
|
|
183
249
|
);
|
|
184
250
|
}
|
|
185
251
|
|
|
186
|
-
private checkBoundary(tooltipElement: HTMLElement) {
|
|
187
|
-
if (!tooltipElement) return;
|
|
188
|
-
|
|
189
|
-
// if data-avoid-collisions does not exist, return
|
|
190
|
-
if (!tooltipElement.hasAttribute("data-avoid-collisions")) return;
|
|
191
|
-
|
|
192
|
-
const viewportWidth = window.innerWidth;
|
|
193
|
-
const tooltipRect = tooltipElement.getBoundingClientRect();
|
|
194
|
-
const padding = 16; // Add some padding from viewport edges
|
|
195
|
-
|
|
196
|
-
// Check if tooltip extends beyond right edge of viewport
|
|
197
|
-
if (tooltipRect.right > viewportWidth - padding) {
|
|
198
|
-
const overflow = tooltipRect.right - (viewportWidth - padding);
|
|
199
|
-
tooltipElement.style.transform = `translateX(-${overflow + padding}px)`;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Check if tooltip extends beyond left edge of viewport
|
|
203
|
-
if (tooltipRect.left < padding) {
|
|
204
|
-
const overflow = padding - tooltipRect.left;
|
|
205
|
-
tooltipElement.style.transform = `translateX(${overflow + padding}px)`;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
252
|
private clearOpenTimer() {
|
|
210
253
|
if (this.openTimerRef) {
|
|
211
254
|
window.clearTimeout(this.openTimerRef);
|
|
@@ -34,43 +34,20 @@ type Props = HTMLAttributes<"div"> & {
|
|
|
34
34
|
export const tooltipContent = tv({
|
|
35
35
|
base: [
|
|
36
36
|
"starwind-tooltip-content",
|
|
37
|
-
"
|
|
37
|
+
"fixed z-50 hidden w-fit px-3 py-1.5",
|
|
38
38
|
"bg-foreground text-background rounded-md",
|
|
39
39
|
"animate-in fade-in zoom-in-95",
|
|
40
40
|
"data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fade-out zoom-out-95",
|
|
41
41
|
],
|
|
42
42
|
variants: {
|
|
43
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
|
-
align: { center: "", start: "", end: "" },
|
|
50
|
-
sideAlign: {
|
|
51
|
-
"top-center": "left-[50%] translate-x-[-50%]",
|
|
52
|
-
"bottom-center": "left-[50%] translate-x-[-50%]",
|
|
53
|
-
"left-center": "top-[50%] translate-y-[-50%]",
|
|
54
|
-
"right-center": "top-[50%] translate-y-[-50%]",
|
|
55
|
-
"top-start": "left-0",
|
|
56
|
-
"bottom-start": "left-0",
|
|
57
|
-
"top-end": "right-0",
|
|
58
|
-
"bottom-end": "right-0",
|
|
59
|
-
"left-start": "top-0",
|
|
60
|
-
"right-start": "top-0",
|
|
61
|
-
"left-end": "bottom-0",
|
|
62
|
-
"right-end": "bottom-0",
|
|
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",
|
|
63
48
|
},
|
|
64
49
|
},
|
|
65
|
-
defaultVariants: { side: "top"
|
|
66
|
-
compoundVariants: [
|
|
67
|
-
{ side: ["top", "bottom"], align: "center", class: "left-[50%] translate-x-[-50%]" },
|
|
68
|
-
{ side: ["left", "right"], align: "center", class: "top-[50%] translate-y-[-50%]" },
|
|
69
|
-
{ side: ["top", "bottom"], align: "start", class: "left-0" },
|
|
70
|
-
{ side: ["top", "bottom"], align: "end", class: "right-0" },
|
|
71
|
-
{ side: ["left", "right"], align: "start", class: "top-0" },
|
|
72
|
-
{ side: ["left", "right"], align: "end", class: "bottom-0" },
|
|
73
|
-
],
|
|
50
|
+
defaultVariants: { side: "top" },
|
|
74
51
|
});
|
|
75
52
|
|
|
76
53
|
export const tooltipCaret = tv({
|
|
@@ -97,17 +74,15 @@ const {
|
|
|
97
74
|
---
|
|
98
75
|
|
|
99
76
|
<div
|
|
100
|
-
class={tooltipContent({ side,
|
|
77
|
+
class={tooltipContent({ side, class: className })}
|
|
101
78
|
data-slot="tooltip-content"
|
|
102
79
|
data-state="closed"
|
|
103
80
|
data-side={side}
|
|
104
81
|
data-align={align}
|
|
82
|
+
data-side-offset={sideOffset}
|
|
105
83
|
{...avoidCollisions && { "data-avoid-collisions": "" }}
|
|
106
84
|
role="tooltip"
|
|
107
|
-
style={{
|
|
108
|
-
"--tooltip-offset": `calc(100% + ${sideOffset}px)`,
|
|
109
|
-
animationDuration: `${animationDuration}ms`,
|
|
110
|
-
}}
|
|
85
|
+
style={{ animationDuration: `${animationDuration}ms` }}
|
|
111
86
|
>
|
|
112
87
|
<slot> My tooltip! </slot>
|
|
113
88
|
<CaretUp class={tooltipCaret({ side })} />
|