@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,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { ComponentProps, HTMLAttributes } from "astro/types";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
import { Skeleton } from "@/components/starwind/skeleton";
|
|
6
|
+
|
|
7
|
+
const sidebarMenuSkeleton = tv({
|
|
8
|
+
base: "flex h-8 items-center gap-2 rounded-md px-2",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
type Props = HTMLAttributes<"div"> & {
|
|
12
|
+
/**
|
|
13
|
+
* Whether to show the icon skeleton
|
|
14
|
+
* @default false
|
|
15
|
+
*/
|
|
16
|
+
showIcon?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Width of the text skeleton (random 50-90% if not specified)
|
|
19
|
+
*/
|
|
20
|
+
width?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const { showIcon = false, width, class: className, ...rest } = Astro.props;
|
|
24
|
+
|
|
25
|
+
const skeletonWidth = width || `${Math.floor(Math.random() * 40) + 50}%`;
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<div
|
|
29
|
+
class={sidebarMenuSkeleton({ class: className })}
|
|
30
|
+
data-slot="sidebar-menu-skeleton"
|
|
31
|
+
data-sidebar="menu-skeleton"
|
|
32
|
+
{...rest}
|
|
33
|
+
>
|
|
34
|
+
{showIcon && <Skeleton class="size-4 rounded-md" data-sidebar="menu-skeleton-icon" />}
|
|
35
|
+
<Skeleton
|
|
36
|
+
class="h-4 max-w-(--skeleton-width) flex-1"
|
|
37
|
+
style={`--skeleton-width: ${skeletonWidth}`}
|
|
38
|
+
data-sidebar="menu-skeleton-text"
|
|
39
|
+
/>
|
|
40
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"ul">;
|
|
6
|
+
|
|
7
|
+
export const sidebarMenuSub = tv({
|
|
8
|
+
base: [
|
|
9
|
+
"border-sidebar-border mx-4.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5",
|
|
10
|
+
"group-data-[collapsible=icon]:hidden",
|
|
11
|
+
],
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const { class: className, ...rest } = Astro.props;
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<ul
|
|
18
|
+
class={sidebarMenuSub({ class: className })}
|
|
19
|
+
data-slot="sidebar-menu-sub"
|
|
20
|
+
data-sidebar="menu-sub"
|
|
21
|
+
{...rest}
|
|
22
|
+
>
|
|
23
|
+
<slot />
|
|
24
|
+
</ul>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"a"> &
|
|
6
|
+
VariantProps<typeof sidebarMenuSubButton> & {
|
|
7
|
+
/**
|
|
8
|
+
* Whether the menu sub item is currently active
|
|
9
|
+
*/
|
|
10
|
+
isActive?: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const sidebarMenuSubButton = tv({
|
|
14
|
+
base: [
|
|
15
|
+
"text-sidebar-foreground ring-sidebar-outline",
|
|
16
|
+
"flex h-8 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2.5",
|
|
17
|
+
"outline-hidden",
|
|
18
|
+
"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
19
|
+
"focus-visible:ring-2",
|
|
20
|
+
"active:bg-sidebar-accent active:text-sidebar-accent-foreground",
|
|
21
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
22
|
+
"aria-disabled:pointer-events-none aria-disabled:opacity-50",
|
|
23
|
+
"[&>svg]:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
|
24
|
+
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
|
|
25
|
+
],
|
|
26
|
+
variants: {
|
|
27
|
+
size: {
|
|
28
|
+
sm: "text-sm",
|
|
29
|
+
md: "text-base",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
size: "md",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const { size, isActive = false, class: className, ...rest } = Astro.props;
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<a
|
|
41
|
+
class={sidebarMenuSubButton({ size, class: className })}
|
|
42
|
+
data-slot="sidebar-menu-sub-button"
|
|
43
|
+
data-sidebar="menu-sub-button"
|
|
44
|
+
data-size={size || "md"}
|
|
45
|
+
data-active={isActive}
|
|
46
|
+
{...rest}
|
|
47
|
+
>
|
|
48
|
+
<slot />
|
|
49
|
+
</a>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
|
|
4
|
+
type Props = HTMLAttributes<"li">;
|
|
5
|
+
|
|
6
|
+
const { class: className, ...rest } = Astro.props;
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<li
|
|
10
|
+
class:list={["group/menu-sub-item relative", className]}
|
|
11
|
+
data-slot="sidebar-menu-sub-item"
|
|
12
|
+
data-sidebar="menu-sub-item"
|
|
13
|
+
{...rest}
|
|
14
|
+
>
|
|
15
|
+
<slot />
|
|
16
|
+
</li>
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"div"> & {
|
|
6
|
+
/**
|
|
7
|
+
* Default open state of the sidebar
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
defaultOpen?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Keyboard shortcut to toggle the sidebar
|
|
13
|
+
* @default "b"
|
|
14
|
+
*/
|
|
15
|
+
keyboardShortcut?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const sidebarProvider = tv({
|
|
19
|
+
base: [
|
|
20
|
+
"starwind-sidebar-provider",
|
|
21
|
+
"group/sidebar-wrapper flex min-h-svh w-full",
|
|
22
|
+
"has-data-[variant=inset]:bg-sidebar",
|
|
23
|
+
],
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
defaultOpen = true,
|
|
28
|
+
keyboardShortcut = "b",
|
|
29
|
+
class: className,
|
|
30
|
+
style,
|
|
31
|
+
...rest
|
|
32
|
+
} = Astro.props;
|
|
33
|
+
|
|
34
|
+
const SIDEBAR_WIDTH = "18rem";
|
|
35
|
+
const SIDEBAR_WIDTH_ICON = "3.5rem";
|
|
36
|
+
|
|
37
|
+
// Parse style prop to extract CSS variable overrides
|
|
38
|
+
const styleObj = typeof style === "string" ? {} : ((style as Record<string, string>) ?? {});
|
|
39
|
+
const mergedStyle = {
|
|
40
|
+
"--sidebar-width": styleObj["--sidebar-width"] ?? SIDEBAR_WIDTH,
|
|
41
|
+
"--sidebar-width-icon": styleObj["--sidebar-width-icon"] ?? SIDEBAR_WIDTH_ICON,
|
|
42
|
+
...styleObj,
|
|
43
|
+
};
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
<div
|
|
47
|
+
class={sidebarProvider({ class: className })}
|
|
48
|
+
data-slot="sidebar-provider"
|
|
49
|
+
data-state={defaultOpen ? "expanded" : "collapsed"}
|
|
50
|
+
data-mobile-open="false"
|
|
51
|
+
data-keyboard-shortcut={keyboardShortcut}
|
|
52
|
+
style={mergedStyle}
|
|
53
|
+
{...rest}
|
|
54
|
+
>
|
|
55
|
+
<slot />
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<script>
|
|
59
|
+
class SidebarController {
|
|
60
|
+
private provider: HTMLElement;
|
|
61
|
+
private boundKeyHandler: (e: KeyboardEvent) => void;
|
|
62
|
+
private boundResizeHandler: () => void;
|
|
63
|
+
private wasMobile: boolean = false;
|
|
64
|
+
|
|
65
|
+
constructor(provider: HTMLElement) {
|
|
66
|
+
this.provider = provider;
|
|
67
|
+
this.boundKeyHandler = this.handleKeydown.bind(this);
|
|
68
|
+
this.boundResizeHandler = this.handleResize.bind(this);
|
|
69
|
+
this.wasMobile = this.isMobile();
|
|
70
|
+
this.setupKeyboardShortcut();
|
|
71
|
+
this.setupCustomEvents();
|
|
72
|
+
this.setupResizeListener();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private handleKeydown(e: KeyboardEvent) {
|
|
76
|
+
// Skip if focus is in an editable element
|
|
77
|
+
const target = e.target as HTMLElement | null;
|
|
78
|
+
if (target) {
|
|
79
|
+
const tagName = target.tagName.toLowerCase();
|
|
80
|
+
if (
|
|
81
|
+
tagName === "input" ||
|
|
82
|
+
tagName === "textarea" ||
|
|
83
|
+
tagName === "select" ||
|
|
84
|
+
target.isContentEditable ||
|
|
85
|
+
target.closest("[contenteditable='true']")
|
|
86
|
+
) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const shortcut = this.provider.getAttribute("data-keyboard-shortcut") || "b";
|
|
92
|
+
if (e.key === shortcut && (e.metaKey || e.ctrlKey)) {
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
this.toggle();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private setupKeyboardShortcut() {
|
|
99
|
+
document.removeEventListener("keydown", this.boundKeyHandler);
|
|
100
|
+
document.addEventListener("keydown", this.boundKeyHandler);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private setupCustomEvents() {
|
|
104
|
+
this.provider.addEventListener("sidebar:toggle", () => this.toggle());
|
|
105
|
+
this.provider.addEventListener("sidebar:open", () => this.setOpen(true));
|
|
106
|
+
this.provider.addEventListener("sidebar:close", () => this.setOpen(false));
|
|
107
|
+
this.provider.addEventListener("sidebar:toggle-mobile", () => this.toggleMobile());
|
|
108
|
+
this.provider.addEventListener("sidebar:open-mobile", () => this.setMobileOpen(true));
|
|
109
|
+
this.provider.addEventListener("sidebar:close-mobile", () => this.setMobileOpen(false));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private handleResize() {
|
|
113
|
+
const isMobileNow = this.isMobile();
|
|
114
|
+
// Close mobile sheet when transitioning from mobile to desktop
|
|
115
|
+
if (this.wasMobile && !isMobileNow) {
|
|
116
|
+
this.setMobileOpen(false);
|
|
117
|
+
}
|
|
118
|
+
this.wasMobile = isMobileNow;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private setupResizeListener() {
|
|
122
|
+
window.removeEventListener("resize", this.boundResizeHandler);
|
|
123
|
+
window.addEventListener("resize", this.boundResizeHandler);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
isMobile(): boolean {
|
|
127
|
+
// Check if desktop sidebar is hidden (has display: none from hidden class)
|
|
128
|
+
const desktopSidebar = this.provider.querySelector<HTMLElement>('[data-slot="sidebar"]');
|
|
129
|
+
if (!desktopSidebar) return false;
|
|
130
|
+
return getComputedStyle(desktopSidebar).display === "none";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
getState(): "expanded" | "collapsed" {
|
|
134
|
+
return this.provider.getAttribute("data-state") as "expanded" | "collapsed";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
isOpen(): boolean {
|
|
138
|
+
return this.getState() === "expanded";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
isMobileOpen(): boolean {
|
|
142
|
+
return this.provider.getAttribute("data-mobile-open") === "true";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
toggle() {
|
|
146
|
+
if (this.isMobile()) {
|
|
147
|
+
this.toggleMobile();
|
|
148
|
+
} else {
|
|
149
|
+
this.setOpen(!this.isOpen());
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
setOpen(open: boolean) {
|
|
154
|
+
const state = open ? "expanded" : "collapsed";
|
|
155
|
+
this.provider.setAttribute("data-state", state);
|
|
156
|
+
this.provider.dispatchEvent(new CustomEvent("sidebar:change", { detail: { open, state } }));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
toggleMobile() {
|
|
160
|
+
// Check the actual dialog state instead of our tracked state
|
|
161
|
+
const mobileSheet = this.provider.querySelector<HTMLElement>(".starwind-sheet");
|
|
162
|
+
const dialogContent = mobileSheet?.querySelector<HTMLElement>(".starwind-dialog-content");
|
|
163
|
+
const isCurrentlyOpen = dialogContent?.dataset.state === "open";
|
|
164
|
+
|
|
165
|
+
this.setMobileOpen(!isCurrentlyOpen);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
setMobileOpen(open: boolean) {
|
|
169
|
+
this.provider.setAttribute("data-mobile-open", String(open));
|
|
170
|
+
|
|
171
|
+
// Find the mobile sidebar Sheet and use Dialog's programmatic API
|
|
172
|
+
const mobileSheet = this.provider.querySelector<HTMLElement>(".starwind-sheet");
|
|
173
|
+
if (mobileSheet) {
|
|
174
|
+
const eventName = open ? "dialog:open" : "dialog:close";
|
|
175
|
+
mobileSheet.dispatchEvent(new CustomEvent(eventName));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this.provider.dispatchEvent(new CustomEvent("sidebar:mobile-change", { detail: { open } }));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
destroy() {
|
|
182
|
+
document.removeEventListener("keydown", this.boundKeyHandler);
|
|
183
|
+
window.removeEventListener("resize", this.boundResizeHandler);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const sidebarInstances = new WeakMap<HTMLElement, SidebarController>();
|
|
188
|
+
const trackedProviders = new Set<HTMLElement>();
|
|
189
|
+
|
|
190
|
+
const cleanupSidebars = () => {
|
|
191
|
+
// Destroy controllers for providers no longer in DOM
|
|
192
|
+
for (const provider of trackedProviders) {
|
|
193
|
+
if (!document.contains(provider)) {
|
|
194
|
+
sidebarInstances.get(provider)?.destroy();
|
|
195
|
+
trackedProviders.delete(provider);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const setupSidebars = () => {
|
|
201
|
+
document.querySelectorAll<HTMLElement>(".starwind-sidebar-provider").forEach((provider) => {
|
|
202
|
+
if (!sidebarInstances.has(provider)) {
|
|
203
|
+
sidebarInstances.set(provider, new SidebarController(provider));
|
|
204
|
+
trackedProviders.add(provider);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
setupSidebars();
|
|
210
|
+
document.addEventListener("astro:before-swap", cleanupSidebars);
|
|
211
|
+
document.addEventListener("astro:after-swap", setupSidebars);
|
|
212
|
+
document.addEventListener("starwind:init", setupSidebars);
|
|
213
|
+
</script>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"button">;
|
|
6
|
+
|
|
7
|
+
export const sidebarRail = tv({
|
|
8
|
+
base: [
|
|
9
|
+
"starwind-sidebar-rail",
|
|
10
|
+
"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear sm:flex",
|
|
11
|
+
"group-data-[side=left]:-right-4 group-data-[side=right]:left-0",
|
|
12
|
+
"after:absolute after:inset-y-0 after:left-1/2 after:w-[2px]",
|
|
13
|
+
"hover:after:bg-sidebar-border",
|
|
14
|
+
"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
|
|
15
|
+
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize",
|
|
16
|
+
"[[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
|
|
17
|
+
"hover:group-data-[collapsible=offcanvas]:bg-sidebar",
|
|
18
|
+
"group-data-[collapsible=offcanvas]:translate-x-0",
|
|
19
|
+
"group-data-[collapsible=offcanvas]:after:left-full",
|
|
20
|
+
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
|
21
|
+
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
|
|
22
|
+
],
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const { class: className, ...rest } = Astro.props;
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<button
|
|
29
|
+
type="button"
|
|
30
|
+
class={sidebarRail({ class: className })}
|
|
31
|
+
data-slot="sidebar-rail"
|
|
32
|
+
data-sidebar="rail"
|
|
33
|
+
aria-label="Toggle Sidebar"
|
|
34
|
+
tabindex="-1"
|
|
35
|
+
{...rest}></button>
|
|
36
|
+
|
|
37
|
+
<script>
|
|
38
|
+
class SidebarRailHandler {
|
|
39
|
+
private rail: HTMLElement;
|
|
40
|
+
private provider: HTMLElement | null;
|
|
41
|
+
|
|
42
|
+
constructor(rail: HTMLElement) {
|
|
43
|
+
this.rail = rail;
|
|
44
|
+
this.provider = rail.closest<HTMLElement>('[data-slot="sidebar-provider"]');
|
|
45
|
+
this.init();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private init() {
|
|
49
|
+
this.rail.addEventListener("click", this.handleClick.bind(this));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private handleClick() {
|
|
53
|
+
if (this.provider) {
|
|
54
|
+
this.provider.dispatchEvent(new CustomEvent("sidebar:toggle"));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const railInstances = new WeakMap<HTMLElement, SidebarRailHandler>();
|
|
60
|
+
|
|
61
|
+
const setupSidebarRails = () => {
|
|
62
|
+
document.querySelectorAll<HTMLElement>(".starwind-sidebar-rail").forEach((rail) => {
|
|
63
|
+
if (railInstances.has(rail)) return;
|
|
64
|
+
railInstances.set(rail, new SidebarRailHandler(rail));
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
setupSidebarRails();
|
|
69
|
+
document.addEventListener("astro:after-swap", setupSidebarRails);
|
|
70
|
+
document.addEventListener("starwind:init", setupSidebarRails);
|
|
71
|
+
</script>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { ComponentProps } from "astro/types";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
import { Separator } from "@/components/starwind/separator";
|
|
6
|
+
|
|
7
|
+
const sidebarSeparator = tv({
|
|
8
|
+
base: "bg-sidebar-border mx-2 w-auto",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
type Props = ComponentProps<typeof Separator>;
|
|
12
|
+
|
|
13
|
+
const { orientation = "horizontal", class: className, ...rest } = Astro.props;
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<Separator
|
|
17
|
+
orientation={orientation}
|
|
18
|
+
class={sidebarSeparator({ class: className })}
|
|
19
|
+
data-slot="sidebar-separator"
|
|
20
|
+
data-sidebar="separator"
|
|
21
|
+
{...rest}
|
|
22
|
+
/>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
import PanelLeft from "@tabler/icons/outline/layout-sidebar.svg";
|
|
3
|
+
import type { ComponentProps } from "astro/types";
|
|
4
|
+
import { tv } from "tailwind-variants";
|
|
5
|
+
|
|
6
|
+
const sidebarTrigger = tv({
|
|
7
|
+
base: "starwind-sidebar-trigger",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
import { Button } from "@/components/starwind/button";
|
|
11
|
+
|
|
12
|
+
type Props = ComponentProps<typeof Button>;
|
|
13
|
+
|
|
14
|
+
const { class: className, size = "icon-sm", variant = "ghost", ...rest } = Astro.props;
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<Button
|
|
18
|
+
variant={variant}
|
|
19
|
+
size={size}
|
|
20
|
+
class={sidebarTrigger({ class: className })}
|
|
21
|
+
data-slot="sidebar-trigger"
|
|
22
|
+
data-sidebar="trigger"
|
|
23
|
+
aria-label="Toggle Sidebar"
|
|
24
|
+
{...rest}
|
|
25
|
+
>
|
|
26
|
+
<slot name="icon">
|
|
27
|
+
<PanelLeft />
|
|
28
|
+
</slot>
|
|
29
|
+
<span class="sr-only">Toggle Sidebar</span>
|
|
30
|
+
</Button>
|
|
31
|
+
|
|
32
|
+
<script>
|
|
33
|
+
class SidebarTriggerHandler {
|
|
34
|
+
private trigger: HTMLElement;
|
|
35
|
+
private provider: HTMLElement | null;
|
|
36
|
+
|
|
37
|
+
constructor(trigger: HTMLElement) {
|
|
38
|
+
this.trigger = trigger;
|
|
39
|
+
this.provider = trigger.closest<HTMLElement>('[data-slot="sidebar-provider"]');
|
|
40
|
+
this.init();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private init() {
|
|
44
|
+
this.trigger.addEventListener("click", this.handleClick.bind(this));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private handleClick() {
|
|
48
|
+
if (this.provider) {
|
|
49
|
+
this.provider.dispatchEvent(new CustomEvent("sidebar:toggle"));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const triggerInstances = new WeakMap<HTMLElement, SidebarTriggerHandler>();
|
|
55
|
+
|
|
56
|
+
const setupSidebarTriggers = () => {
|
|
57
|
+
document.querySelectorAll<HTMLElement>(".starwind-sidebar-trigger").forEach((trigger) => {
|
|
58
|
+
if (triggerInstances.has(trigger)) return;
|
|
59
|
+
triggerInstances.set(trigger, new SidebarTriggerHandler(trigger));
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
setupSidebarTriggers();
|
|
64
|
+
document.addEventListener("astro:after-swap", setupSidebarTriggers);
|
|
65
|
+
document.addEventListener("starwind:init", setupSidebarTriggers);
|
|
66
|
+
</script>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import Sidebar, {
|
|
2
|
+
sidebar,
|
|
3
|
+
sidebarContainer,
|
|
4
|
+
sidebarGap,
|
|
5
|
+
sidebarInner,
|
|
6
|
+
sidebarMobileContent,
|
|
7
|
+
} from "./Sidebar.astro";
|
|
8
|
+
import SidebarContent, { sidebarContent } from "./SidebarContent.astro";
|
|
9
|
+
import SidebarFooter, { sidebarFooter } from "./SidebarFooter.astro";
|
|
10
|
+
import SidebarGroup, { sidebarGroup } from "./SidebarGroup.astro";
|
|
11
|
+
import SidebarGroupContent, { sidebarGroupContent } from "./SidebarGroupContent.astro";
|
|
12
|
+
import SidebarGroupLabel, { sidebarGroupLabel } from "./SidebarGroupLabel.astro";
|
|
13
|
+
import SidebarHeader, { sidebarHeader } from "./SidebarHeader.astro";
|
|
14
|
+
import SidebarInput from "./SidebarInput.astro";
|
|
15
|
+
import SidebarInset, { sidebarInset } from "./SidebarInset.astro";
|
|
16
|
+
import SidebarMenu, { sidebarMenu } from "./SidebarMenu.astro";
|
|
17
|
+
import SidebarMenuAction, { sidebarMenuAction } from "./SidebarMenuAction.astro";
|
|
18
|
+
import SidebarMenuBadge, { sidebarMenuBadge } from "./SidebarMenuBadge.astro";
|
|
19
|
+
import SidebarMenuButton, { sidebarMenuButton } from "./SidebarMenuButton.astro";
|
|
20
|
+
import SidebarMenuItem, { sidebarMenuItem } from "./SidebarMenuItem.astro";
|
|
21
|
+
import SidebarMenuSkeleton from "./SidebarMenuSkeleton.astro";
|
|
22
|
+
import SidebarMenuSub, { sidebarMenuSub } from "./SidebarMenuSub.astro";
|
|
23
|
+
import SidebarMenuSubButton, { sidebarMenuSubButton } from "./SidebarMenuSubButton.astro";
|
|
24
|
+
import SidebarMenuSubItem from "./SidebarMenuSubItem.astro";
|
|
25
|
+
import SidebarProvider, { sidebarProvider } from "./SidebarProvider.astro";
|
|
26
|
+
import SidebarRail, { sidebarRail } from "./SidebarRail.astro";
|
|
27
|
+
import SidebarSeparator from "./SidebarSeparator.astro";
|
|
28
|
+
import SidebarTrigger from "./SidebarTrigger.astro";
|
|
29
|
+
|
|
30
|
+
const SidebarVariants = {
|
|
31
|
+
sidebar,
|
|
32
|
+
sidebarGap,
|
|
33
|
+
sidebarContainer,
|
|
34
|
+
sidebarInner,
|
|
35
|
+
sidebarMobileContent,
|
|
36
|
+
sidebarContent,
|
|
37
|
+
sidebarFooter,
|
|
38
|
+
sidebarGroup,
|
|
39
|
+
sidebarGroupContent,
|
|
40
|
+
sidebarGroupLabel,
|
|
41
|
+
sidebarHeader,
|
|
42
|
+
sidebarInset,
|
|
43
|
+
sidebarMenu,
|
|
44
|
+
sidebarMenuAction,
|
|
45
|
+
sidebarMenuBadge,
|
|
46
|
+
sidebarMenuButton,
|
|
47
|
+
sidebarMenuItem,
|
|
48
|
+
sidebarMenuSub,
|
|
49
|
+
sidebarMenuSubButton,
|
|
50
|
+
sidebarProvider,
|
|
51
|
+
sidebarRail,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
Sidebar,
|
|
56
|
+
SidebarContent,
|
|
57
|
+
SidebarFooter,
|
|
58
|
+
SidebarGroup,
|
|
59
|
+
SidebarGroupContent,
|
|
60
|
+
SidebarGroupLabel,
|
|
61
|
+
SidebarHeader,
|
|
62
|
+
SidebarInput,
|
|
63
|
+
SidebarInset,
|
|
64
|
+
SidebarMenu,
|
|
65
|
+
SidebarMenuAction,
|
|
66
|
+
SidebarMenuBadge,
|
|
67
|
+
SidebarMenuButton,
|
|
68
|
+
SidebarMenuItem,
|
|
69
|
+
SidebarMenuSkeleton,
|
|
70
|
+
SidebarMenuSub,
|
|
71
|
+
SidebarMenuSubButton,
|
|
72
|
+
SidebarMenuSubItem,
|
|
73
|
+
SidebarProvider,
|
|
74
|
+
SidebarRail,
|
|
75
|
+
SidebarSeparator,
|
|
76
|
+
SidebarTrigger,
|
|
77
|
+
SidebarVariants,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export default {
|
|
81
|
+
Root: SidebarProvider,
|
|
82
|
+
Sidebar,
|
|
83
|
+
Content: SidebarContent,
|
|
84
|
+
Footer: SidebarFooter,
|
|
85
|
+
Group: SidebarGroup,
|
|
86
|
+
GroupContent: SidebarGroupContent,
|
|
87
|
+
GroupLabel: SidebarGroupLabel,
|
|
88
|
+
Header: SidebarHeader,
|
|
89
|
+
Input: SidebarInput,
|
|
90
|
+
Inset: SidebarInset,
|
|
91
|
+
Menu: SidebarMenu,
|
|
92
|
+
MenuAction: SidebarMenuAction,
|
|
93
|
+
MenuBadge: SidebarMenuBadge,
|
|
94
|
+
MenuButton: SidebarMenuButton,
|
|
95
|
+
MenuItem: SidebarMenuItem,
|
|
96
|
+
MenuSkeleton: SidebarMenuSkeleton,
|
|
97
|
+
MenuSub: SidebarMenuSub,
|
|
98
|
+
MenuSubButton: SidebarMenuSubButton,
|
|
99
|
+
MenuSubItem: SidebarMenuSubItem,
|
|
100
|
+
Rail: SidebarRail,
|
|
101
|
+
Separator: SidebarSeparator,
|
|
102
|
+
Trigger: SidebarTrigger,
|
|
103
|
+
};
|