@starwind-ui/core 1.11.2 → 1.12.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.
- package/package.json +1 -1
- package/dist/index.d.ts +0 -28
- package/dist/index.js +0 -74
- package/dist/index.js.map +0 -1
- package/dist/src/components/accordion/Accordion.astro +0 -247
- package/dist/src/components/accordion/AccordionContent.astro +0 -33
- package/dist/src/components/accordion/AccordionItem.astro +0 -27
- package/dist/src/components/accordion/AccordionTrigger.astro +0 -32
- package/dist/src/components/accordion/index.ts +0 -15
- package/dist/src/components/alert/Alert.astro +0 -31
- package/dist/src/components/alert/AlertDescription.astro +0 -14
- package/dist/src/components/alert/AlertTitle.astro +0 -16
- package/dist/src/components/alert/index.ts +0 -13
- package/dist/src/components/alert-dialog/AlertDialog.astro +0 -273
- package/dist/src/components/alert-dialog/AlertDialogAction.astro +0 -44
- package/dist/src/components/alert-dialog/AlertDialogCancel.astro +0 -45
- package/dist/src/components/alert-dialog/AlertDialogContent.astro +0 -52
- package/dist/src/components/alert-dialog/AlertDialogDescription.astro +0 -18
- package/dist/src/components/alert-dialog/AlertDialogFooter.astro +0 -16
- package/dist/src/components/alert-dialog/AlertDialogHeader.astro +0 -14
- package/dist/src/components/alert-dialog/AlertDialogTitle.astro +0 -20
- package/dist/src/components/alert-dialog/AlertDialogTrigger.astro +0 -47
- package/dist/src/components/alert-dialog/index.ts +0 -46
- package/dist/src/components/aspect-ratio/AspectRatio.astro +0 -32
- package/dist/src/components/aspect-ratio/index.ts +0 -7
- package/dist/src/components/avatar/Avatar.astro +0 -29
- package/dist/src/components/avatar/AvatarFallback.astro +0 -18
- package/dist/src/components/avatar/AvatarImage.astro +0 -49
- package/dist/src/components/avatar/index.ts +0 -13
- package/dist/src/components/badge/Badge.astro +0 -51
- package/dist/src/components/badge/index.ts +0 -7
- package/dist/src/components/breadcrumb/Breadcrumb.astro +0 -11
- package/dist/src/components/breadcrumb/BreadcrumbEllipsis.astro +0 -28
- package/dist/src/components/breadcrumb/BreadcrumbItem.astro +0 -14
- package/dist/src/components/breadcrumb/BreadcrumbLink.astro +0 -22
- package/dist/src/components/breadcrumb/BreadcrumbList.astro +0 -16
- package/dist/src/components/breadcrumb/BreadcrumbPage.astro +0 -21
- package/dist/src/components/breadcrumb/BreadcrumbSeparator.astro +0 -23
- package/dist/src/components/breadcrumb/index.ts +0 -37
- package/dist/src/components/button/Button.astro +0 -53
- package/dist/src/components/button/index.ts +0 -7
- package/dist/src/components/card/Card.astro +0 -14
- package/dist/src/components/card/CardContent.astro +0 -14
- package/dist/src/components/card/CardDescription.astro +0 -14
- package/dist/src/components/card/CardFooter.astro +0 -14
- package/dist/src/components/card/CardHeader.astro +0 -14
- package/dist/src/components/card/CardTitle.astro +0 -14
- package/dist/src/components/card/index.ts +0 -26
- package/dist/src/components/carousel/Carousel.astro +0 -55
- package/dist/src/components/carousel/CarouselContent.astro +0 -26
- package/dist/src/components/carousel/CarouselItem.astro +0 -26
- package/dist/src/components/carousel/CarouselNext.astro +0 -37
- package/dist/src/components/carousel/CarouselPrevious.astro +0 -37
- package/dist/src/components/carousel/carousel-script.ts +0 -191
- package/dist/src/components/carousel/index.ts +0 -32
- package/dist/src/components/checkbox/Checkbox.astro +0 -127
- package/dist/src/components/checkbox/index.ts +0 -7
- package/dist/src/components/dialog/Dialog.astro +0 -263
- package/dist/src/components/dialog/DialogClose.astro +0 -35
- package/dist/src/components/dialog/DialogContent.astro +0 -67
- package/dist/src/components/dialog/DialogDescription.astro +0 -14
- package/dist/src/components/dialog/DialogFooter.astro +0 -14
- package/dist/src/components/dialog/DialogHeader.astro +0 -14
- package/dist/src/components/dialog/DialogTitle.astro +0 -20
- package/dist/src/components/dialog/DialogTrigger.astro +0 -47
- package/dist/src/components/dialog/index.ts +0 -45
- package/dist/src/components/dropdown/Dropdown.astro +0 -375
- package/dist/src/components/dropdown/DropdownContent.astro +0 -81
- package/dist/src/components/dropdown/DropdownItem.astro +0 -48
- package/dist/src/components/dropdown/DropdownLabel.astro +0 -29
- package/dist/src/components/dropdown/DropdownSeparator.astro +0 -21
- package/dist/src/components/dropdown/DropdownTrigger.astro +0 -52
- package/dist/src/components/dropdown/index.ts +0 -33
- package/dist/src/components/dropzone/Dropzone.astro +0 -233
- package/dist/src/components/dropzone/DropzoneFilesList.astro +0 -26
- package/dist/src/components/dropzone/DropzoneLoadingIndicator.astro +0 -10
- package/dist/src/components/dropzone/DropzoneUploadIndicator.astro +0 -10
- package/dist/src/components/dropzone/index.ts +0 -24
- package/dist/src/components/input/Input.astro +0 -24
- package/dist/src/components/input/index.ts +0 -7
- package/dist/src/components/item/Item.astro +0 -52
- package/dist/src/components/item/ItemActions.astro +0 -16
- package/dist/src/components/item/ItemContent.astro +0 -16
- package/dist/src/components/item/ItemDescription.astro +0 -19
- package/dist/src/components/item/ItemFooter.astro +0 -16
- package/dist/src/components/item/ItemGroup.astro +0 -16
- package/dist/src/components/item/ItemHeader.astro +0 -16
- package/dist/src/components/item/ItemMedia.astro +0 -40
- package/dist/src/components/item/ItemSeparator.astro +0 -21
- package/dist/src/components/item/ItemTitle.astro +0 -16
- package/dist/src/components/item/index.ts +0 -50
- package/dist/src/components/kbd/Kbd.astro +0 -21
- package/dist/src/components/kbd/KbdGroup.astro +0 -16
- package/dist/src/components/kbd/index.ts +0 -11
- package/dist/src/components/label/Label.astro +0 -22
- package/dist/src/components/label/index.ts +0 -7
- package/dist/src/components/pagination/Pagination.astro +0 -20
- package/dist/src/components/pagination/PaginationContent.astro +0 -16
- package/dist/src/components/pagination/PaginationEllipsis.astro +0 -25
- package/dist/src/components/pagination/PaginationItem.astro +0 -16
- package/dist/src/components/pagination/PaginationLink.astro +0 -24
- package/dist/src/components/pagination/PaginationNext.astro +0 -26
- package/dist/src/components/pagination/PaginationPrevious.astro +0 -26
- package/dist/src/components/pagination/index.ts +0 -38
- package/dist/src/components/progress/Progress.astro +0 -154
- package/dist/src/components/progress/index.ts +0 -10
- package/dist/src/components/radio-group/RadioGroup.astro +0 -157
- package/dist/src/components/radio-group/RadioGroupItem.astro +0 -129
- package/dist/src/components/radio-group/RadioGroupTypes.ts +0 -6
- package/dist/src/components/radio-group/index.ts +0 -23
- package/dist/src/components/select/Select.astro +0 -534
- package/dist/src/components/select/SelectContent.astro +0 -83
- package/dist/src/components/select/SelectGroup.astro +0 -9
- package/dist/src/components/select/SelectItem.astro +0 -49
- package/dist/src/components/select/SelectLabel.astro +0 -14
- package/dist/src/components/select/SelectSeparator.astro +0 -12
- package/dist/src/components/select/SelectTrigger.astro +0 -48
- package/dist/src/components/select/SelectTypes.ts +0 -13
- package/dist/src/components/select/SelectValue.astro +0 -19
- package/dist/src/components/select/index.ts +0 -45
- package/dist/src/components/separator/Separator.astro +0 -36
- package/dist/src/components/separator/index.ts +0 -7
- package/dist/src/components/sheet/Sheet.astro +0 -13
- package/dist/src/components/sheet/SheetClose.astro +0 -13
- package/dist/src/components/sheet/SheetContent.astro +0 -92
- package/dist/src/components/sheet/SheetDescription.astro +0 -16
- package/dist/src/components/sheet/SheetFooter.astro +0 -16
- package/dist/src/components/sheet/SheetHeader.astro +0 -16
- package/dist/src/components/sheet/SheetTitle.astro +0 -16
- package/dist/src/components/sheet/SheetTrigger.astro +0 -13
- package/dist/src/components/sheet/index.ts +0 -41
- package/dist/src/components/skeleton/Skeleton.astro +0 -14
- package/dist/src/components/skeleton/index.ts +0 -9
- package/dist/src/components/spinner/Spinner.astro +0 -21
- package/dist/src/components/spinner/index.ts +0 -7
- package/dist/src/components/switch/Switch.astro +0 -191
- package/dist/src/components/switch/SwitchTypes.ts +0 -6
- package/dist/src/components/switch/index.ts +0 -12
- package/dist/src/components/table/Table.astro +0 -18
- package/dist/src/components/table/TableBody.astro +0 -16
- package/dist/src/components/table/TableCaption.astro +0 -16
- package/dist/src/components/table/TableCell.astro +0 -16
- package/dist/src/components/table/TableFoot.astro +0 -16
- package/dist/src/components/table/TableHead.astro +0 -16
- package/dist/src/components/table/TableHeader.astro +0 -16
- package/dist/src/components/table/TableRow.astro +0 -16
- package/dist/src/components/table/index.ts +0 -42
- package/dist/src/components/tabs/Tabs.astro +0 -269
- package/dist/src/components/tabs/TabsContent.astro +0 -28
- package/dist/src/components/tabs/TabsList.astro +0 -22
- package/dist/src/components/tabs/TabsTrigger.astro +0 -34
- package/dist/src/components/tabs/index.ts +0 -20
- package/dist/src/components/textarea/Textarea.astro +0 -28
- package/dist/src/components/textarea/index.ts +0 -9
- package/dist/src/components/tooltip/Tooltip.astro +0 -237
- package/dist/src/components/tooltip/TooltipContent.astro +0 -114
- package/dist/src/components/tooltip/TooltipTrigger.astro +0 -10
- package/dist/src/components/tooltip/index.ts +0 -16
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import X from "@tabler/icons/outline/x.svg";
|
|
3
|
-
import type { HTMLAttributes } from "astro/types";
|
|
4
|
-
import { tv } from "tailwind-variants";
|
|
5
|
-
|
|
6
|
-
type Props = HTMLAttributes<"dialog"> & {
|
|
7
|
-
/**
|
|
8
|
-
* Open and close animation duration in milliseconds
|
|
9
|
-
*/
|
|
10
|
-
animationDuration?: number;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const dialogBackdrop = tv({
|
|
14
|
-
base: [
|
|
15
|
-
"starwind-dialog-backdrop fixed inset-0 top-0 left-0 z-50 hidden h-screen w-screen bg-black/80",
|
|
16
|
-
"data-[state=open]:animate-in fade-in",
|
|
17
|
-
"data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fade-out",
|
|
18
|
-
],
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export const dialogContent = tv({
|
|
22
|
-
base: [
|
|
23
|
-
"fixed top-16 left-[50%] z-50 translate-x-[-50%] sm:top-[50%] sm:translate-y-[-50%]",
|
|
24
|
-
"bg-background w-full max-w-md border p-8 shadow-lg sm:rounded-lg",
|
|
25
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards",
|
|
26
|
-
"fade-in zoom-in-95 slide-in-from-bottom-2",
|
|
27
|
-
"fade-out zoom-out-95 slide-out-to-bottom-2",
|
|
28
|
-
],
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
export const dialogCloseButton = tv({
|
|
32
|
-
base: [
|
|
33
|
-
"starwind-dialog-close text-muted-foreground",
|
|
34
|
-
"absolute top-5.5 right-5.5 rounded-sm [&>svg]:opacity-70 hover:[&>svg]:opacity-100",
|
|
35
|
-
"focus-visible:ring-outline/50 transition-[color,box-shadow] outline-none focus-visible:ring-3",
|
|
36
|
-
],
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const { class: className, animationDuration = 200, ...rest } = Astro.props;
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
<!-- dialog overlay -->
|
|
43
|
-
<slot name="backdrop">
|
|
44
|
-
<div
|
|
45
|
-
class={dialogBackdrop()}
|
|
46
|
-
data-state="closed"
|
|
47
|
-
data-slot="dialog-backdrop"
|
|
48
|
-
style={{ animationDuration: `${animationDuration}ms` }}
|
|
49
|
-
>
|
|
50
|
-
</div>
|
|
51
|
-
</slot>
|
|
52
|
-
|
|
53
|
-
<dialog
|
|
54
|
-
class={dialogContent({ class: className })}
|
|
55
|
-
data-state="closed"
|
|
56
|
-
data-slot="dialog-content"
|
|
57
|
-
{...rest}
|
|
58
|
-
style={{ animationDuration: `${animationDuration}ms` }}
|
|
59
|
-
>
|
|
60
|
-
<slot />
|
|
61
|
-
<button type="button" class={dialogCloseButton()} data-dialog-close aria-label="Close dialog">
|
|
62
|
-
<slot name="icon">
|
|
63
|
-
<X class="size-5 transition-opacity" />
|
|
64
|
-
</slot>
|
|
65
|
-
<span class="sr-only">Close</span>
|
|
66
|
-
</button>
|
|
67
|
-
</dialog>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from "astro/types";
|
|
3
|
-
import { tv } from "tailwind-variants";
|
|
4
|
-
|
|
5
|
-
type Props = HTMLAttributes<"p">;
|
|
6
|
-
|
|
7
|
-
export const dialogDescription = tv({ base: "text-muted-foreground" });
|
|
8
|
-
|
|
9
|
-
const { class: className, ...rest } = Astro.props;
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
<p class={dialogDescription({ class: className })} data-slot="dialog-description" {...rest}>
|
|
13
|
-
<slot />
|
|
14
|
-
</p>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from "astro/types";
|
|
3
|
-
import { tv } from "tailwind-variants";
|
|
4
|
-
|
|
5
|
-
type Props = HTMLAttributes<"div">;
|
|
6
|
-
|
|
7
|
-
export const dialogFooter = tv({ base: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end" });
|
|
8
|
-
|
|
9
|
-
const { class: className, ...rest } = Astro.props;
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
<div class={dialogFooter({ class: className })} data-slot="dialog-footer" {...rest}>
|
|
13
|
-
<slot />
|
|
14
|
-
</div>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from "astro/types";
|
|
3
|
-
import { tv } from "tailwind-variants";
|
|
4
|
-
|
|
5
|
-
type Props = HTMLAttributes<"div">;
|
|
6
|
-
|
|
7
|
-
export const dialogHeader = tv({ base: "flex flex-col space-y-2 text-center sm:text-left" });
|
|
8
|
-
|
|
9
|
-
const { class: className, ...rest } = Astro.props;
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
<div class={dialogHeader({ class: className })} data-slot="dialog-header" {...rest}>
|
|
13
|
-
<slot />
|
|
14
|
-
</div>
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from "astro/types";
|
|
3
|
-
import { tv } from "tailwind-variants";
|
|
4
|
-
|
|
5
|
-
type Props = Omit<HTMLAttributes<"h2">, "id"> & {
|
|
6
|
-
/**
|
|
7
|
-
* The content to be rendered inside the dialog title
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
children: any;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const dialogTitle = tv({ base: "text-xl leading-none font-semibold tracking-tight" });
|
|
14
|
-
|
|
15
|
-
const { class: className, ...rest } = Astro.props;
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
<h2 class={dialogTitle({ class: className })} data-slot="dialog-title" {...rest}>
|
|
19
|
-
<slot />
|
|
20
|
-
</h2>
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from "astro/types";
|
|
3
|
-
|
|
4
|
-
type Props = HTMLAttributes<"button"> & {
|
|
5
|
-
/**
|
|
6
|
-
* When true, the component will render its child element with a simple wrapper instead of a button component
|
|
7
|
-
*/
|
|
8
|
-
asChild?: boolean;
|
|
9
|
-
/**
|
|
10
|
-
* Optional ID of the dialog to trigger. If not provided and the trigger is inside a Dialog component,
|
|
11
|
-
* it will automatically target that dialog. Required when used outside a Dialog component.
|
|
12
|
-
*/
|
|
13
|
-
for?: string;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const { class: className, asChild = false, for: dialogFor, ...rest } = Astro.props;
|
|
17
|
-
|
|
18
|
-
// Get the first child element if asChild is true
|
|
19
|
-
let hasChildren = false;
|
|
20
|
-
if (Astro.slots.has("default")) {
|
|
21
|
-
hasChildren = true;
|
|
22
|
-
}
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
{
|
|
26
|
-
asChild && hasChildren ? (
|
|
27
|
-
<div
|
|
28
|
-
class:list={["starwind-dialog-trigger", className]}
|
|
29
|
-
data-slot="dialog-trigger"
|
|
30
|
-
data-as-child
|
|
31
|
-
data-dialog-for={dialogFor}
|
|
32
|
-
>
|
|
33
|
-
<slot />
|
|
34
|
-
</div>
|
|
35
|
-
) : (
|
|
36
|
-
<button
|
|
37
|
-
type="button"
|
|
38
|
-
aria-haspopup="dialog"
|
|
39
|
-
class:list={["starwind-dialog-trigger", className]}
|
|
40
|
-
data-slot="dialog-trigger"
|
|
41
|
-
data-dialog-for={dialogFor}
|
|
42
|
-
{...rest}
|
|
43
|
-
>
|
|
44
|
-
<slot />
|
|
45
|
-
</button>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import Dialog from "./Dialog.astro";
|
|
2
|
-
import DialogClose from "./DialogClose.astro";
|
|
3
|
-
import DialogContent, {
|
|
4
|
-
dialogBackdrop,
|
|
5
|
-
dialogCloseButton,
|
|
6
|
-
dialogContent,
|
|
7
|
-
} from "./DialogContent.astro";
|
|
8
|
-
import DialogDescription, { dialogDescription } from "./DialogDescription.astro";
|
|
9
|
-
import DialogFooter, { dialogFooter } from "./DialogFooter.astro";
|
|
10
|
-
import DialogHeader, { dialogHeader } from "./DialogHeader.astro";
|
|
11
|
-
import DialogTitle, { dialogTitle } from "./DialogTitle.astro";
|
|
12
|
-
import DialogTrigger from "./DialogTrigger.astro";
|
|
13
|
-
|
|
14
|
-
const DialogVariants = {
|
|
15
|
-
dialogBackdrop,
|
|
16
|
-
dialogContent,
|
|
17
|
-
dialogCloseButton,
|
|
18
|
-
dialogDescription,
|
|
19
|
-
dialogFooter,
|
|
20
|
-
dialogHeader,
|
|
21
|
-
dialogTitle,
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export {
|
|
25
|
-
Dialog,
|
|
26
|
-
DialogClose,
|
|
27
|
-
DialogContent,
|
|
28
|
-
DialogDescription,
|
|
29
|
-
DialogFooter,
|
|
30
|
-
DialogHeader,
|
|
31
|
-
DialogTitle,
|
|
32
|
-
DialogTrigger,
|
|
33
|
-
DialogVariants,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export default {
|
|
37
|
-
Root: Dialog,
|
|
38
|
-
Trigger: DialogTrigger,
|
|
39
|
-
Content: DialogContent,
|
|
40
|
-
Header: DialogHeader,
|
|
41
|
-
Footer: DialogFooter,
|
|
42
|
-
Title: DialogTitle,
|
|
43
|
-
Description: DialogDescription,
|
|
44
|
-
Close: DialogClose,
|
|
45
|
-
};
|
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from "astro/types";
|
|
3
|
-
|
|
4
|
-
type Props = HTMLAttributes<"div"> & {
|
|
5
|
-
/**
|
|
6
|
-
* When true, the dropdown will open on hover in addition to click
|
|
7
|
-
*/
|
|
8
|
-
openOnHover?: boolean;
|
|
9
|
-
/**
|
|
10
|
-
* Time in milliseconds to wait before closing when hover open is enabled
|
|
11
|
-
* @default 200
|
|
12
|
-
*/
|
|
13
|
-
closeDelay?: number;
|
|
14
|
-
|
|
15
|
-
children: any;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const { class: className, openOnHover = false, closeDelay = 200, ...rest } = Astro.props;
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
<div
|
|
22
|
-
class:list={["starwind-dropdown", "relative", className]}
|
|
23
|
-
data-open-on-hover={openOnHover ? "true" : undefined}
|
|
24
|
-
data-close-delay={closeDelay}
|
|
25
|
-
data-slot="dropdown"
|
|
26
|
-
{...rest}
|
|
27
|
-
>
|
|
28
|
-
<slot />
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
<script>
|
|
32
|
-
class DropdownHandler {
|
|
33
|
-
private dropdown: HTMLElement;
|
|
34
|
-
private trigger: HTMLButtonElement | null;
|
|
35
|
-
private content: HTMLElement | null;
|
|
36
|
-
private items: HTMLElement[] = [];
|
|
37
|
-
private currentFocusIndex: number = -1;
|
|
38
|
-
private isOpen: boolean = false;
|
|
39
|
-
private isClosing: boolean = false;
|
|
40
|
-
private animationDuration = 150;
|
|
41
|
-
private openOnHover: boolean;
|
|
42
|
-
private closeDelay: number;
|
|
43
|
-
private closeTimerRef: number | null = null;
|
|
44
|
-
private lastOpenSource: "keyboard" | "mouse" = "keyboard";
|
|
45
|
-
private lastCloseSource: "keyboard" | "mouse" = "keyboard";
|
|
46
|
-
|
|
47
|
-
constructor(dropdown: HTMLElement, dropdownIdx: number) {
|
|
48
|
-
this.dropdown = dropdown;
|
|
49
|
-
this.openOnHover = dropdown.getAttribute("data-open-on-hover") === "true";
|
|
50
|
-
this.closeDelay = parseInt(dropdown.getAttribute("data-close-delay") || "200");
|
|
51
|
-
|
|
52
|
-
// Get the temporary trigger element
|
|
53
|
-
const tempTrigger = dropdown.querySelector(".starwind-dropdown-trigger") as HTMLElement;
|
|
54
|
-
|
|
55
|
-
// if trigger is set with asChild, use the first child element for trigger button
|
|
56
|
-
if (tempTrigger?.hasAttribute("data-as-child")) {
|
|
57
|
-
this.trigger = tempTrigger.firstElementChild as HTMLButtonElement;
|
|
58
|
-
} else {
|
|
59
|
-
this.trigger = tempTrigger as HTMLButtonElement;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
this.content = dropdown.querySelector(".starwind-dropdown-content");
|
|
63
|
-
|
|
64
|
-
if (!this.trigger || !this.content) return;
|
|
65
|
-
|
|
66
|
-
// Get animation duration from inline styles if available
|
|
67
|
-
const animationDurationString = this.content.style.animationDuration;
|
|
68
|
-
if (animationDurationString.endsWith("ms")) {
|
|
69
|
-
this.animationDuration = parseFloat(animationDurationString);
|
|
70
|
-
} else if (animationDurationString.endsWith("s")) {
|
|
71
|
-
this.animationDuration = parseFloat(animationDurationString) * 1000;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
this.init(dropdownIdx);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
private init(dropdownIdx: number) {
|
|
78
|
-
this.setupAccessibility(dropdownIdx);
|
|
79
|
-
this.setupEvents();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
private setupAccessibility(dropdownIdx: number) {
|
|
83
|
-
if (!this.trigger || !this.content) return;
|
|
84
|
-
|
|
85
|
-
// Generate unique IDs for accessibility
|
|
86
|
-
this.trigger.id = `starwind-dropdown${dropdownIdx}-trigger`;
|
|
87
|
-
this.content.id = `starwind-dropdown${dropdownIdx}-content`;
|
|
88
|
-
|
|
89
|
-
// Set up additional ARIA attributes
|
|
90
|
-
this.trigger.setAttribute("aria-controls", this.content.id);
|
|
91
|
-
this.content.setAttribute("aria-labelledby", this.trigger.id);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
private setupEvents() {
|
|
95
|
-
if (!this.trigger || !this.content) return;
|
|
96
|
-
|
|
97
|
-
// Handle trigger click
|
|
98
|
-
this.trigger.addEventListener("click", (e) => {
|
|
99
|
-
e.preventDefault();
|
|
100
|
-
this.lastOpenSource = e.detail === 0 ? "keyboard" : "mouse";
|
|
101
|
-
this.toggleDropdown();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// Handle keyboard navigation
|
|
105
|
-
this.trigger.addEventListener("keydown", (e) => {
|
|
106
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
107
|
-
e.preventDefault();
|
|
108
|
-
this.lastOpenSource = "keyboard";
|
|
109
|
-
this.toggleDropdown();
|
|
110
|
-
} else if (e.key === "Escape" && this.isOpen) {
|
|
111
|
-
e.preventDefault();
|
|
112
|
-
this.lastCloseSource = "keyboard";
|
|
113
|
-
this.closeDropdown();
|
|
114
|
-
} else if (this.isOpen && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
|
|
115
|
-
e.preventDefault();
|
|
116
|
-
this.lastOpenSource = "keyboard";
|
|
117
|
-
this.updateDropdownItems();
|
|
118
|
-
if (e.key === "ArrowDown") {
|
|
119
|
-
this.focusItem(0); // Focus first item when opening with arrow down
|
|
120
|
-
} else {
|
|
121
|
-
this.focusItem(this.items.length - 1); // Focus last item when opening with arrow up
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// Close dropdown when clicking outside for mouse
|
|
127
|
-
document.addEventListener("pointerdown", (e) => {
|
|
128
|
-
if (this.isOpen && !this.dropdown.contains(e.target as Node)) {
|
|
129
|
-
// only call handler if it's the left button (mousedown gets triggered by all mouse buttons)
|
|
130
|
-
// but not when the control key is pressed (avoiding MacOS right click); also not for touch
|
|
131
|
-
// devices because that would open the menu on scroll. (pen devices behave as touch on iOS).
|
|
132
|
-
if (e.button === 0 && e.ctrlKey === false && e.pointerType === "mouse") {
|
|
133
|
-
this.closeDropdown();
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Handle click outside select content to close for mobile
|
|
139
|
-
document.addEventListener("click", (e) => {
|
|
140
|
-
if (
|
|
141
|
-
!(this.trigger?.contains(e.target as Node) || this.content?.contains(e.target as Node)) &&
|
|
142
|
-
this.isOpen
|
|
143
|
-
) {
|
|
144
|
-
this.closeDropdown();
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Handle keyboard navigation and item selection within dropdown
|
|
149
|
-
this.content.addEventListener("keydown", (e) => {
|
|
150
|
-
if (e.key === "Escape") {
|
|
151
|
-
e.preventDefault();
|
|
152
|
-
this.closeDropdown();
|
|
153
|
-
this.trigger?.focus();
|
|
154
|
-
} else if (this.isOpen) {
|
|
155
|
-
this.handleMenuKeydown(e);
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Handle item selection
|
|
160
|
-
this.content.addEventListener("click", (e) => {
|
|
161
|
-
const target = e.target as HTMLElement;
|
|
162
|
-
const item = target.closest('[role="menuitem"]');
|
|
163
|
-
if (item && !(item as HTMLElement).hasAttribute("data-disabled")) {
|
|
164
|
-
// Close the dropdown after item selection
|
|
165
|
-
this.closeDropdown();
|
|
166
|
-
// console.log("click closing");
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
// Handle hover on dropdown items
|
|
171
|
-
this.content.addEventListener("mouseover", (e) => {
|
|
172
|
-
const target = e.target as HTMLElement;
|
|
173
|
-
const menuItem = target.closest('[role="menuitem"]');
|
|
174
|
-
if (menuItem && menuItem instanceof HTMLElement && this.isOpen === true) {
|
|
175
|
-
// Update items list before focusing to ensure the index is correct
|
|
176
|
-
this.updateDropdownItems();
|
|
177
|
-
|
|
178
|
-
// Focus the item when hovering
|
|
179
|
-
menuItem.focus();
|
|
180
|
-
|
|
181
|
-
// Update the current focus index
|
|
182
|
-
this.currentFocusIndex = this.items.indexOf(menuItem);
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
if (this.openOnHover) {
|
|
187
|
-
this.trigger.addEventListener("pointerenter", (e) => {
|
|
188
|
-
if (e.pointerType !== "mouse") return;
|
|
189
|
-
if (this.isClosing) return;
|
|
190
|
-
if (!this.isOpen) {
|
|
191
|
-
this.lastOpenSource = "mouse";
|
|
192
|
-
this.openDropdown();
|
|
193
|
-
} else {
|
|
194
|
-
// If the dropdown is already open, make sure to clear any close timer
|
|
195
|
-
this.clearCloseTimer();
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
this.dropdown.addEventListener("pointerleave", (e) => {
|
|
200
|
-
if (e.pointerType !== "mouse") return;
|
|
201
|
-
if (this.isOpen) {
|
|
202
|
-
this.lastCloseSource = "mouse";
|
|
203
|
-
this.closeDropdownDelayed();
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
this.content.addEventListener("pointerenter", (e) => {
|
|
208
|
-
if (e.pointerType !== "mouse") return;
|
|
209
|
-
// If the user moves the mouse to the content, cancel the close timer
|
|
210
|
-
this.clearCloseTimer();
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
private handleMenuKeydown(e: KeyboardEvent) {
|
|
216
|
-
// Make sure we've got an updated list of menu items
|
|
217
|
-
this.updateDropdownItems();
|
|
218
|
-
|
|
219
|
-
// Skip if no items
|
|
220
|
-
if (this.items.length === 0) return;
|
|
221
|
-
|
|
222
|
-
const currentIdx = this.currentFocusIndex;
|
|
223
|
-
|
|
224
|
-
switch (e.key) {
|
|
225
|
-
case "ArrowDown":
|
|
226
|
-
e.preventDefault();
|
|
227
|
-
this.focusItem(currentIdx === -1 ? 0 : currentIdx + 1);
|
|
228
|
-
break;
|
|
229
|
-
case "ArrowUp":
|
|
230
|
-
e.preventDefault();
|
|
231
|
-
this.focusItem(currentIdx === -1 ? this.items.length - 1 : currentIdx - 1);
|
|
232
|
-
break;
|
|
233
|
-
case "Home":
|
|
234
|
-
e.preventDefault();
|
|
235
|
-
this.focusItem(0);
|
|
236
|
-
break;
|
|
237
|
-
case "End":
|
|
238
|
-
e.preventDefault();
|
|
239
|
-
this.focusItem(this.items.length - 1);
|
|
240
|
-
break;
|
|
241
|
-
case "Enter":
|
|
242
|
-
case " ":
|
|
243
|
-
if (currentIdx !== -1) {
|
|
244
|
-
e.preventDefault();
|
|
245
|
-
this.items[currentIdx].click();
|
|
246
|
-
}
|
|
247
|
-
break;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
private updateDropdownItems() {
|
|
252
|
-
if (!this.content) return;
|
|
253
|
-
// Get all interactive menuitem elements
|
|
254
|
-
this.items = Array.from(
|
|
255
|
-
this.content.querySelectorAll('[role="menuitem"]:not([data-disabled="true"])'),
|
|
256
|
-
) as HTMLElement[];
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
private focusItem(idx: number) {
|
|
260
|
-
// Ensure the index wraps around properly
|
|
261
|
-
const targetIdx = (idx + this.items.length) % this.items.length;
|
|
262
|
-
|
|
263
|
-
if (this.items[targetIdx]) {
|
|
264
|
-
this.items[targetIdx].focus();
|
|
265
|
-
this.currentFocusIndex = targetIdx;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
private toggleDropdown() {
|
|
270
|
-
if (this.isOpen) {
|
|
271
|
-
this.closeDropdown();
|
|
272
|
-
} else {
|
|
273
|
-
this.openDropdown();
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
private openDropdown() {
|
|
278
|
-
if (this.isClosing) return;
|
|
279
|
-
if (!this.content || !this.trigger || this.trigger.disabled) return;
|
|
280
|
-
|
|
281
|
-
this.isOpen = true;
|
|
282
|
-
this.content.setAttribute("data-state", "open");
|
|
283
|
-
this.trigger.setAttribute("aria-expanded", "true");
|
|
284
|
-
this.content.style.removeProperty("display");
|
|
285
|
-
|
|
286
|
-
// Update the list of dropdown items
|
|
287
|
-
this.updateDropdownItems();
|
|
288
|
-
|
|
289
|
-
// Reset focus index when opening
|
|
290
|
-
this.currentFocusIndex = -1;
|
|
291
|
-
|
|
292
|
-
this.positionContent();
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
private closeDropdown() {
|
|
296
|
-
if (!this.content || !this.trigger) return;
|
|
297
|
-
|
|
298
|
-
this.isClosing = true;
|
|
299
|
-
this.isOpen = false;
|
|
300
|
-
this.content.setAttribute("data-state", "closed");
|
|
301
|
-
|
|
302
|
-
// Set focus back on trigger only if opened or closed by keyboard
|
|
303
|
-
if (
|
|
304
|
-
!this.openOnHover ||
|
|
305
|
-
this.lastOpenSource === "keyboard" ||
|
|
306
|
-
this.lastCloseSource === "keyboard"
|
|
307
|
-
) {
|
|
308
|
-
requestAnimationFrame(() => {
|
|
309
|
-
if (!this.trigger) return;
|
|
310
|
-
this.trigger.focus();
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Give the content time to animate before hiding
|
|
315
|
-
setTimeout(() => {
|
|
316
|
-
if (!this.content) return;
|
|
317
|
-
this.content.style.display = "none";
|
|
318
|
-
this.isClosing = false;
|
|
319
|
-
}, this.animationDuration);
|
|
320
|
-
|
|
321
|
-
this.trigger.setAttribute("aria-expanded", "false");
|
|
322
|
-
|
|
323
|
-
// Reset focus index when closing
|
|
324
|
-
this.currentFocusIndex = -1;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
private closeDropdownDelayed() {
|
|
328
|
-
if (!this.content || !this.trigger) return;
|
|
329
|
-
|
|
330
|
-
// Clear any existing close timer
|
|
331
|
-
this.clearCloseTimer();
|
|
332
|
-
|
|
333
|
-
// Set a new timer to close the dropdown after the delay
|
|
334
|
-
this.closeTimerRef = window.setTimeout(() => {
|
|
335
|
-
if (this.isOpen) {
|
|
336
|
-
this.closeDropdown();
|
|
337
|
-
}
|
|
338
|
-
this.closeTimerRef = null;
|
|
339
|
-
}, this.closeDelay);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
private clearCloseTimer() {
|
|
343
|
-
if (this.closeTimerRef !== null) {
|
|
344
|
-
window.clearTimeout(this.closeTimerRef);
|
|
345
|
-
this.closeTimerRef = null;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
private positionContent() {
|
|
350
|
-
if (!this.content || !this.trigger) return;
|
|
351
|
-
|
|
352
|
-
// Set content width to match trigger width
|
|
353
|
-
this.content.style.width = "var(--starwind-dropdown-trigger-width)";
|
|
354
|
-
this.content.style.setProperty(
|
|
355
|
-
"--starwind-dropdown-trigger-width",
|
|
356
|
-
`${this.trigger.offsetWidth}px`,
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Store instances in a WeakMap to avoid memory leaks
|
|
362
|
-
const dropdownInstances = new WeakMap<HTMLElement, DropdownHandler>();
|
|
363
|
-
|
|
364
|
-
// Initialize dropdowns
|
|
365
|
-
const initDropdowns = () => {
|
|
366
|
-
document.querySelectorAll(".starwind-dropdown").forEach((dropdown, idx) => {
|
|
367
|
-
if (dropdown instanceof HTMLElement && !dropdownInstances.has(dropdown)) {
|
|
368
|
-
dropdownInstances.set(dropdown, new DropdownHandler(dropdown, idx));
|
|
369
|
-
}
|
|
370
|
-
});
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
initDropdowns();
|
|
374
|
-
document.addEventListener("astro:after-swap", initDropdowns);
|
|
375
|
-
</script>
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from "astro/types";
|
|
3
|
-
import { tv } from "tailwind-variants";
|
|
4
|
-
|
|
5
|
-
type Props = HTMLAttributes<"div"> & {
|
|
6
|
-
/**
|
|
7
|
-
* Side of the dropdown
|
|
8
|
-
* @default bottom
|
|
9
|
-
*/
|
|
10
|
-
side?: "top" | "bottom";
|
|
11
|
-
/**
|
|
12
|
-
* Alignment of the dropdown
|
|
13
|
-
* @default start
|
|
14
|
-
*/
|
|
15
|
-
align?: "start" | "center" | "end";
|
|
16
|
-
/**
|
|
17
|
-
* Offset distance in pixels
|
|
18
|
-
* @default 4
|
|
19
|
-
*/
|
|
20
|
-
sideOffset?: number;
|
|
21
|
-
/**
|
|
22
|
-
* Open and close animation duration in milliseconds
|
|
23
|
-
* @default 150
|
|
24
|
-
*/
|
|
25
|
-
animationDuration?: number;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const dropdownContent = tv({
|
|
29
|
-
base: [
|
|
30
|
-
"starwind-dropdown-content",
|
|
31
|
-
"bg-popover text-popover-foreground z-50 min-w-[9rem] overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
|
32
|
-
"data-[state=open]:animate-in fade-in zoom-in-95",
|
|
33
|
-
"data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fade-out zoom-out-95",
|
|
34
|
-
"absolute will-change-transform",
|
|
35
|
-
],
|
|
36
|
-
variants: {
|
|
37
|
-
side: {
|
|
38
|
-
bottom: "slide-in-from-top-2 slide-out-to-top-2 top-full",
|
|
39
|
-
top: "slide-in-from-bottom-2 slide-out-to-bottom-2 bottom-full",
|
|
40
|
-
},
|
|
41
|
-
align: {
|
|
42
|
-
start: "slide-in-from-left-1 slide-out-to-left-1 left-0",
|
|
43
|
-
center: "left-1/2 -translate-x-1/2",
|
|
44
|
-
end: "slide-in-from-right-1 slide-out-to-right-1 right-0",
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
defaultVariants: {
|
|
48
|
-
side: "bottom",
|
|
49
|
-
align: "start",
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const {
|
|
54
|
-
class: className,
|
|
55
|
-
side = "bottom",
|
|
56
|
-
align = "start",
|
|
57
|
-
sideOffset = 4,
|
|
58
|
-
animationDuration = 150,
|
|
59
|
-
...rest
|
|
60
|
-
} = Astro.props;
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
<div
|
|
64
|
-
class={dropdownContent({ side, align, class: className })}
|
|
65
|
-
role="menu"
|
|
66
|
-
data-side={side}
|
|
67
|
-
data-align={align}
|
|
68
|
-
data-state="closed"
|
|
69
|
-
data-slot="dropdown-content"
|
|
70
|
-
tabindex="-1"
|
|
71
|
-
aria-orientation="vertical"
|
|
72
|
-
style={{
|
|
73
|
-
display: "none",
|
|
74
|
-
animationDuration: `${animationDuration}ms`,
|
|
75
|
-
marginTop: side === "bottom" ? `${sideOffset}px` : undefined,
|
|
76
|
-
marginBottom: side === "top" ? `${sideOffset}px` : undefined,
|
|
77
|
-
}}
|
|
78
|
-
{...rest}
|
|
79
|
-
>
|
|
80
|
-
<slot />
|
|
81
|
-
</div>
|