@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,37 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import ArrowLeft from "@tabler/icons/outline/arrow-left.svg";
|
|
3
|
-
import type { ComponentProps } from "astro/types";
|
|
4
|
-
import { tv } from "tailwind-variants";
|
|
5
|
-
|
|
6
|
-
import { Button } from "@/components/starwind/button";
|
|
7
|
-
|
|
8
|
-
export const carouselPrevious = tv({
|
|
9
|
-
base: [
|
|
10
|
-
"starwind-carousel-previous absolute size-8 rounded-full",
|
|
11
|
-
// Horizontal positioning
|
|
12
|
-
"group-data-[axis=x]/carousel:top-1/2 group-data-[axis=x]/carousel:-left-12 group-data-[axis=x]/carousel:-translate-y-1/2",
|
|
13
|
-
// Vertical positioning
|
|
14
|
-
"group-data-[axis=y]/carousel:-top-12 group-data-[axis=y]/carousel:left-1/2 group-data-[axis=y]/carousel:-translate-x-1/2 group-data-[axis=y]/carousel:rotate-90",
|
|
15
|
-
],
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
type Props = ComponentProps<typeof Button>;
|
|
19
|
-
|
|
20
|
-
const { class: className = "", variant = "outline", size = "icon", ...rest } = Astro.props;
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
<Button
|
|
24
|
-
data-slot="carousel-previous"
|
|
25
|
-
variant={variant}
|
|
26
|
-
size={size}
|
|
27
|
-
class={carouselPrevious({ class: className })}
|
|
28
|
-
aria-label="Previous slide"
|
|
29
|
-
{...rest}
|
|
30
|
-
>
|
|
31
|
-
<slot name="icon">
|
|
32
|
-
<ArrowLeft />
|
|
33
|
-
</slot>
|
|
34
|
-
<slot>
|
|
35
|
-
<span class="sr-only">Previous slide</span>
|
|
36
|
-
</slot>
|
|
37
|
-
</Button>
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import EmblaCarousel, {
|
|
2
|
-
type EmblaCarouselType,
|
|
3
|
-
type EmblaEventType,
|
|
4
|
-
type EmblaOptionsType,
|
|
5
|
-
type EmblaPluginType,
|
|
6
|
-
} from "embla-carousel";
|
|
7
|
-
|
|
8
|
-
export type CarouselApi = EmblaCarouselType;
|
|
9
|
-
|
|
10
|
-
export interface CarouselOptions {
|
|
11
|
-
opts?: EmblaOptionsType;
|
|
12
|
-
plugins?: EmblaPluginType[];
|
|
13
|
-
setApi?: (api: CarouselApi) => void;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface CarouselManager {
|
|
17
|
-
api: CarouselApi;
|
|
18
|
-
scrollPrev: () => void;
|
|
19
|
-
scrollNext: () => void;
|
|
20
|
-
canScrollPrev: () => boolean;
|
|
21
|
-
canScrollNext: () => boolean;
|
|
22
|
-
destroy: () => void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function initCarousel(
|
|
26
|
-
carouselElement: HTMLElement,
|
|
27
|
-
options: CarouselOptions = {},
|
|
28
|
-
): CarouselManager | null {
|
|
29
|
-
// don't re-initialize if already initialized
|
|
30
|
-
if (carouselElement.dataset.initialized === "true") return null;
|
|
31
|
-
carouselElement.dataset.initialized = "true";
|
|
32
|
-
|
|
33
|
-
if (!carouselElement) {
|
|
34
|
-
console.warn("Carousel element not found");
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Find content element - Embla expects the viewport element, not the container
|
|
39
|
-
const viewportElement = carouselElement.querySelector(
|
|
40
|
-
'[data-slot="carousel-content"]',
|
|
41
|
-
) as HTMLElement;
|
|
42
|
-
if (!viewportElement) {
|
|
43
|
-
console.warn("Carousel content element not found");
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Get configuration from data attributes
|
|
48
|
-
const axisData = carouselElement.dataset.axis;
|
|
49
|
-
const axis: EmblaOptionsType["axis"] = axisData === "y" ? "y" : "x";
|
|
50
|
-
|
|
51
|
-
// Safely parse data options
|
|
52
|
-
let dataOpts = {};
|
|
53
|
-
try {
|
|
54
|
-
const optsString = carouselElement.dataset.opts;
|
|
55
|
-
if (optsString && optsString !== "undefined" && optsString !== "null") {
|
|
56
|
-
dataOpts = JSON.parse(optsString);
|
|
57
|
-
}
|
|
58
|
-
} catch (e) {
|
|
59
|
-
console.warn("Failed to parse carousel opts:", e);
|
|
60
|
-
dataOpts = {};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Ensure dataOpts is a valid object
|
|
64
|
-
if (!dataOpts || typeof dataOpts !== "object") {
|
|
65
|
-
dataOpts = {};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Merge options - ensure we always have a valid object
|
|
69
|
-
const emblaOptions: EmblaOptionsType = {
|
|
70
|
-
axis,
|
|
71
|
-
...dataOpts,
|
|
72
|
-
...(options.opts || {}),
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Handle plugins - EmblaCarousel expects undefined when no plugins, not empty array
|
|
76
|
-
const plugins = options.plugins && options.plugins.length > 0 ? options.plugins : undefined;
|
|
77
|
-
|
|
78
|
-
// console.log("ID:", carouselElement.id);
|
|
79
|
-
// console.log("Plugins:", plugins);
|
|
80
|
-
// console.log("Options:", emblaOptions);
|
|
81
|
-
|
|
82
|
-
// Find navigation buttons
|
|
83
|
-
const prevButton = carouselElement.querySelector(
|
|
84
|
-
'[data-slot="carousel-previous"]',
|
|
85
|
-
) as HTMLButtonElement;
|
|
86
|
-
const nextButton = carouselElement.querySelector(
|
|
87
|
-
'[data-slot="carousel-next"]',
|
|
88
|
-
) as HTMLButtonElement;
|
|
89
|
-
|
|
90
|
-
// Initialize Embla
|
|
91
|
-
let emblaApi: EmblaCarouselType;
|
|
92
|
-
if (plugins) {
|
|
93
|
-
emblaApi = EmblaCarousel(viewportElement, emblaOptions, plugins);
|
|
94
|
-
} else {
|
|
95
|
-
emblaApi = EmblaCarousel(viewportElement, emblaOptions);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Update button states
|
|
99
|
-
const updateButtons = () => {
|
|
100
|
-
const canScrollPrev = emblaApi.canScrollPrev();
|
|
101
|
-
const canScrollNext = emblaApi.canScrollNext();
|
|
102
|
-
|
|
103
|
-
if (prevButton) {
|
|
104
|
-
prevButton.disabled = !canScrollPrev;
|
|
105
|
-
prevButton.setAttribute("aria-disabled", (!canScrollPrev).toString());
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (nextButton) {
|
|
109
|
-
nextButton.disabled = !canScrollNext;
|
|
110
|
-
nextButton.setAttribute("aria-disabled", (!canScrollNext).toString());
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
// Event handlers for cleanup
|
|
115
|
-
const prevClickHandler = () => emblaApi.scrollPrev();
|
|
116
|
-
const nextClickHandler = () => emblaApi.scrollNext();
|
|
117
|
-
const keydownHandler = (event: KeyboardEvent) => {
|
|
118
|
-
if (axis === "y") {
|
|
119
|
-
// Vertical axis: ArrowUp = previous, ArrowDown = next
|
|
120
|
-
if (event.key === "ArrowUp") {
|
|
121
|
-
event.preventDefault();
|
|
122
|
-
emblaApi.scrollPrev();
|
|
123
|
-
} else if (event.key === "ArrowDown") {
|
|
124
|
-
event.preventDefault();
|
|
125
|
-
emblaApi.scrollNext();
|
|
126
|
-
}
|
|
127
|
-
} else {
|
|
128
|
-
// Horizontal axis (default): ArrowLeft = previous, ArrowRight = next
|
|
129
|
-
if (event.key === "ArrowLeft") {
|
|
130
|
-
event.preventDefault();
|
|
131
|
-
emblaApi.scrollPrev();
|
|
132
|
-
} else if (event.key === "ArrowRight") {
|
|
133
|
-
event.preventDefault();
|
|
134
|
-
emblaApi.scrollNext();
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
// Setup event listeners
|
|
140
|
-
const setupEventListeners = () => {
|
|
141
|
-
// Navigation button listeners
|
|
142
|
-
prevButton?.addEventListener("click", prevClickHandler);
|
|
143
|
-
nextButton?.addEventListener("click", nextClickHandler);
|
|
144
|
-
|
|
145
|
-
// Keyboard navigation
|
|
146
|
-
carouselElement.addEventListener("keydown", keydownHandler);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
// Setup user API callback
|
|
150
|
-
const setupUserCallbacks = () => {
|
|
151
|
-
if (options.setApi) {
|
|
152
|
-
options.setApi(emblaApi);
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
// Initialize everything
|
|
157
|
-
updateButtons();
|
|
158
|
-
setupEventListeners();
|
|
159
|
-
setupUserCallbacks();
|
|
160
|
-
|
|
161
|
-
// Setup internal event listeners
|
|
162
|
-
emblaApi.on("select", updateButtons);
|
|
163
|
-
emblaApi.on("init", () => {
|
|
164
|
-
updateButtons();
|
|
165
|
-
});
|
|
166
|
-
emblaApi.on("reInit", () => {
|
|
167
|
-
updateButtons();
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
// Return manager interface
|
|
171
|
-
return {
|
|
172
|
-
api: emblaApi,
|
|
173
|
-
scrollPrev: () => emblaApi.scrollPrev(),
|
|
174
|
-
scrollNext: () => emblaApi.scrollNext(),
|
|
175
|
-
canScrollPrev: () => emblaApi.canScrollPrev(),
|
|
176
|
-
canScrollNext: () => emblaApi.canScrollNext(),
|
|
177
|
-
destroy: () => {
|
|
178
|
-
// Remove event listeners to prevent memory leaks
|
|
179
|
-
if (prevButton) {
|
|
180
|
-
prevButton.removeEventListener("click", prevClickHandler);
|
|
181
|
-
}
|
|
182
|
-
if (nextButton) {
|
|
183
|
-
nextButton.removeEventListener("click", nextClickHandler);
|
|
184
|
-
}
|
|
185
|
-
carouselElement.removeEventListener("keydown", keydownHandler);
|
|
186
|
-
|
|
187
|
-
// Destroy the Embla instance
|
|
188
|
-
emblaApi.destroy();
|
|
189
|
-
},
|
|
190
|
-
};
|
|
191
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import Carousel from "./Carousel.astro";
|
|
2
|
-
import {
|
|
3
|
-
type CarouselApi,
|
|
4
|
-
type CarouselManager,
|
|
5
|
-
type CarouselOptions,
|
|
6
|
-
initCarousel,
|
|
7
|
-
} from "./carousel-script";
|
|
8
|
-
import CarouselContent from "./CarouselContent.astro";
|
|
9
|
-
import CarouselItem from "./CarouselItem.astro";
|
|
10
|
-
import CarouselNext from "./CarouselNext.astro";
|
|
11
|
-
import CarouselPrevious from "./CarouselPrevious.astro";
|
|
12
|
-
|
|
13
|
-
export {
|
|
14
|
-
Carousel,
|
|
15
|
-
type CarouselApi,
|
|
16
|
-
CarouselContent,
|
|
17
|
-
CarouselItem,
|
|
18
|
-
type CarouselManager,
|
|
19
|
-
CarouselNext,
|
|
20
|
-
type CarouselOptions,
|
|
21
|
-
CarouselPrevious,
|
|
22
|
-
initCarousel,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export default {
|
|
26
|
-
Root: Carousel,
|
|
27
|
-
Content: CarouselContent,
|
|
28
|
-
Item: CarouselItem,
|
|
29
|
-
Next: CarouselNext,
|
|
30
|
-
Previous: CarouselPrevious,
|
|
31
|
-
init: initCarousel,
|
|
32
|
-
};
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import Check from "@tabler/icons/outline/check.svg";
|
|
3
|
-
import type { HTMLAttributes } from "astro/types";
|
|
4
|
-
import { tv, type VariantProps } from "tailwind-variants";
|
|
5
|
-
|
|
6
|
-
type Props = Omit<HTMLAttributes<"input">, "type"> &
|
|
7
|
-
VariantProps<typeof checkbox> & {
|
|
8
|
-
/**
|
|
9
|
-
* Optional label text to display next to the checkbox
|
|
10
|
-
*/
|
|
11
|
-
label?: string;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export const checkbox = tv({
|
|
15
|
-
slots: {
|
|
16
|
-
base: "starwind-checkbox relative flex items-center space-x-2",
|
|
17
|
-
input: [
|
|
18
|
-
"peer border-input bg-background dark:bg-input/30 shrink-0 transform-gpu rounded-sm border",
|
|
19
|
-
"transition-all focus-visible:ring-3",
|
|
20
|
-
"outline-0 focus:ring-0 focus:ring-offset-0",
|
|
21
|
-
"not-disabled:cursor-pointer disabled:cursor-not-allowed disabled:opacity-50",
|
|
22
|
-
],
|
|
23
|
-
icon: [
|
|
24
|
-
"pointer-events-none absolute stroke-3 p-0.5 opacity-0 transition-opacity peer-checked:opacity-100",
|
|
25
|
-
"starwind-check-icon",
|
|
26
|
-
],
|
|
27
|
-
label:
|
|
28
|
-
"font-medium peer-not-disabled:cursor-pointer peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
29
|
-
},
|
|
30
|
-
variants: {
|
|
31
|
-
size: {
|
|
32
|
-
sm: { input: "size-4", icon: "size-4", label: "text-sm" },
|
|
33
|
-
md: { input: "size-5", icon: "size-5", label: "text-base" },
|
|
34
|
-
lg: { input: "size-6", icon: "size-6", label: "text-lg" },
|
|
35
|
-
},
|
|
36
|
-
variant: {
|
|
37
|
-
default: {
|
|
38
|
-
input: "checked:bg-foreground focus-visible:ring-outline/50 focus-visible:border-outline",
|
|
39
|
-
icon: "text-background",
|
|
40
|
-
},
|
|
41
|
-
primary: {
|
|
42
|
-
input:
|
|
43
|
-
"checked:bg-primary checked:border-primary focus-visible:ring-primary/50 focus-visible:border-primary",
|
|
44
|
-
icon: "text-primary-foreground",
|
|
45
|
-
},
|
|
46
|
-
secondary: {
|
|
47
|
-
input:
|
|
48
|
-
"checked:bg-secondary checked:border-secondary focus-visible:ring-secondary/50 focus-visible:border-secondary",
|
|
49
|
-
icon: "text-secondary-foreground",
|
|
50
|
-
},
|
|
51
|
-
info: {
|
|
52
|
-
input:
|
|
53
|
-
"checked:bg-info checked:border-info focus-visible:ring-info/50 focus-visible:border-info",
|
|
54
|
-
icon: "text-info-foreground",
|
|
55
|
-
},
|
|
56
|
-
success: {
|
|
57
|
-
input:
|
|
58
|
-
"checked:bg-success checked:border-success focus-visible:ring-success/50 focus-visible:border-success",
|
|
59
|
-
icon: "text-success-foreground",
|
|
60
|
-
},
|
|
61
|
-
warning: {
|
|
62
|
-
input:
|
|
63
|
-
"checked:bg-warning checked:border-warning focus-visible:ring-warning/50 focus-visible:border-warning",
|
|
64
|
-
icon: "text-warning-foreground",
|
|
65
|
-
},
|
|
66
|
-
error: {
|
|
67
|
-
input:
|
|
68
|
-
"checked:bg-error checked:border-error focus-visible:ring-error/50 focus-visible:border-error",
|
|
69
|
-
icon: "text-error-foreground",
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
defaultVariants: { size: "md", variant: "default" },
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const { id, label, checked, size, variant, class: className, ...rest } = Astro.props;
|
|
77
|
-
|
|
78
|
-
const { base, input, icon, label: labelClass } = checkbox({ size, variant });
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
<div class={base()}>
|
|
82
|
-
<input
|
|
83
|
-
type="checkbox"
|
|
84
|
-
id={id}
|
|
85
|
-
class={input({ class: className })}
|
|
86
|
-
data-slot="checkbox-input"
|
|
87
|
-
{checked}
|
|
88
|
-
{...rest}
|
|
89
|
-
/>
|
|
90
|
-
<Check class={icon()} />
|
|
91
|
-
{
|
|
92
|
-
label && (
|
|
93
|
-
<label for={id} class={labelClass()} data-slot="checkbox-label">
|
|
94
|
-
{label}
|
|
95
|
-
</label>
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
</div>
|
|
99
|
-
|
|
100
|
-
<style>
|
|
101
|
-
.starwind-checkbox input[type="checkbox"]:checked {
|
|
102
|
-
background-image: none;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/* Check drawing animation */
|
|
106
|
-
.starwind-check-icon {
|
|
107
|
-
stroke-dasharray: 65;
|
|
108
|
-
stroke-dashoffset: 65;
|
|
109
|
-
opacity: 0;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
.starwind-checkbox input[type="checkbox"]:checked + .starwind-check-icon {
|
|
113
|
-
animation: draw-check 0.3s ease forwards;
|
|
114
|
-
animation-delay: 0.15s;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
@keyframes draw-check {
|
|
118
|
-
0% {
|
|
119
|
-
stroke-dashoffset: 65;
|
|
120
|
-
opacity: 1;
|
|
121
|
-
}
|
|
122
|
-
100% {
|
|
123
|
-
stroke-dashoffset: 0;
|
|
124
|
-
opacity: 1;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
</style>
|
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from "astro/types";
|
|
3
|
-
|
|
4
|
-
type Props = HTMLAttributes<"div">;
|
|
5
|
-
|
|
6
|
-
const { class: className, ...rest } = Astro.props;
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
<div class:list={["starwind-dialog", className]} data-slot="dialog" {...rest}>
|
|
10
|
-
<slot />
|
|
11
|
-
</div>
|
|
12
|
-
|
|
13
|
-
<script>
|
|
14
|
-
class DialogHandler {
|
|
15
|
-
private triggers: HTMLButtonElement[] = [];
|
|
16
|
-
private dialog: HTMLDialogElement | null = null;
|
|
17
|
-
private closeButtons: HTMLButtonElement[] = [];
|
|
18
|
-
private backdrop: HTMLElement | null = null;
|
|
19
|
-
private dialogId: string;
|
|
20
|
-
/**
|
|
21
|
-
* The duration of the animation in milliseconds. This is used to calculate the
|
|
22
|
-
* duration of close animation before hiding the dialog and backdrop
|
|
23
|
-
*/
|
|
24
|
-
private animationDuration: number;
|
|
25
|
-
|
|
26
|
-
constructor(dialogWrapper: HTMLElement, dialogNumber: number) {
|
|
27
|
-
this.dialog = dialogWrapper.querySelector("dialog");
|
|
28
|
-
this.backdrop = dialogWrapper.querySelector(".starwind-dialog-backdrop");
|
|
29
|
-
if (!this.dialog || !this.backdrop) {
|
|
30
|
-
throw new Error("Dialog: dialog or backdrop not found");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// if no ID was provided for the wrapper, generate one
|
|
34
|
-
if (dialogWrapper.id) {
|
|
35
|
-
this.dialogId = dialogWrapper.id;
|
|
36
|
-
} else {
|
|
37
|
-
this.dialogId = `starwind-dialog${dialogNumber}`;
|
|
38
|
-
dialogWrapper.id = this.dialogId;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// animationDuration is set with inline styles through passed prop to DialogContent
|
|
42
|
-
// if no animationDuration, check data-close-duration
|
|
43
|
-
const animationDurationString = this.dialog.style.animationDuration;
|
|
44
|
-
if (animationDurationString.endsWith("ms")) {
|
|
45
|
-
this.animationDuration = parseFloat(animationDurationString);
|
|
46
|
-
} else if (animationDurationString.endsWith("s")) {
|
|
47
|
-
// using something like @playform/compress might optimize to use "s" instead of "ms"
|
|
48
|
-
this.animationDuration = parseFloat(animationDurationString) * 1000;
|
|
49
|
-
} else {
|
|
50
|
-
this.animationDuration = this.dialog.dataset.closeDuration
|
|
51
|
-
? parseFloat(this.dialog.dataset.closeDuration)
|
|
52
|
-
: 200;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Find internal triggers and handle them
|
|
56
|
-
const internalTriggers = dialogWrapper.querySelectorAll(".starwind-dialog-trigger");
|
|
57
|
-
internalTriggers.forEach((triggerElement) => {
|
|
58
|
-
const tempTrigger = triggerElement as HTMLElement;
|
|
59
|
-
let trigger: HTMLButtonElement;
|
|
60
|
-
|
|
61
|
-
if (tempTrigger?.hasAttribute("data-as-child")) {
|
|
62
|
-
trigger = tempTrigger.firstElementChild as HTMLButtonElement;
|
|
63
|
-
} else {
|
|
64
|
-
trigger = tempTrigger as HTMLButtonElement;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (trigger) {
|
|
68
|
-
this.triggers.push(trigger);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Find external triggers that target this dialog
|
|
73
|
-
this.findExternalTriggers();
|
|
74
|
-
|
|
75
|
-
// if closeButtons are set with asChild, swap the wrapper with its first child
|
|
76
|
-
const tempCloseButtons = dialogWrapper.querySelectorAll(
|
|
77
|
-
".starwind-dialog-close",
|
|
78
|
-
) as NodeListOf<HTMLElement>;
|
|
79
|
-
tempCloseButtons.forEach((button: HTMLElement) => {
|
|
80
|
-
if (button.hasAttribute("data-as-child")) {
|
|
81
|
-
const childElement = button.firstElementChild;
|
|
82
|
-
if (childElement) {
|
|
83
|
-
childElement.classList.add("starwind-dialog-close");
|
|
84
|
-
button.parentNode?.replaceChild(childElement, button);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return button;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// Convert NodeList to Array for consistency with triggers
|
|
91
|
-
this.closeButtons = Array.from(
|
|
92
|
-
dialogWrapper.querySelectorAll(".starwind-dialog-close"),
|
|
93
|
-
) as HTMLButtonElement[];
|
|
94
|
-
|
|
95
|
-
// if essential elements are not there, exit
|
|
96
|
-
if (!this.dialog || !this.backdrop) return;
|
|
97
|
-
|
|
98
|
-
this.setupAccessibility(dialogNumber);
|
|
99
|
-
this.setupEvents();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private setupAccessibility(dialogNumber: number): void {
|
|
103
|
-
// get the first heading element in the dialog
|
|
104
|
-
const firstHeading = this.dialog?.querySelector("h1, h2, h3, h4, h5, h6");
|
|
105
|
-
if (firstHeading) {
|
|
106
|
-
// create a unique ID for the heading
|
|
107
|
-
firstHeading.id = `starwind-dialog${dialogNumber}-heading`;
|
|
108
|
-
// set the aria-labelledby attribute to the first heading element
|
|
109
|
-
this.dialog?.setAttribute("aria-labelledby", firstHeading.id);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Find all external triggers that target this dialog
|
|
115
|
-
*/
|
|
116
|
-
private findExternalTriggers(): void {
|
|
117
|
-
const externalTriggers = document.querySelectorAll(
|
|
118
|
-
`.starwind-dialog-trigger[data-dialog-for="${this.dialogId}"]`,
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
externalTriggers.forEach((triggerElement) => {
|
|
122
|
-
// Skip if this is an internal trigger we already processed
|
|
123
|
-
const dialogWrapper = triggerElement.closest(".starwind-dialog");
|
|
124
|
-
if (dialogWrapper && dialogWrapper.id === this.dialogId) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
let trigger: HTMLButtonElement;
|
|
129
|
-
if (triggerElement.hasAttribute("data-as-child")) {
|
|
130
|
-
trigger = triggerElement.firstElementChild as HTMLButtonElement;
|
|
131
|
-
} else {
|
|
132
|
-
trigger = triggerElement as HTMLButtonElement;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (trigger && !this.triggers.includes(trigger)) {
|
|
136
|
-
this.triggers.push(trigger);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
private setupEvents(): void {
|
|
142
|
-
if (!this.dialog) return;
|
|
143
|
-
// Add click listeners to all triggers
|
|
144
|
-
this.triggers.forEach((trigger) => {
|
|
145
|
-
trigger.addEventListener("click", () => {
|
|
146
|
-
this.open();
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// Add click handlers to all close buttons
|
|
151
|
-
this.closeButtons?.forEach((button) => {
|
|
152
|
-
button.addEventListener("click", () => {
|
|
153
|
-
// Only close if this is the topmost dialog
|
|
154
|
-
const openDialogs = document.querySelectorAll("dialog[open]");
|
|
155
|
-
if (openDialogs.length > 0 && openDialogs[openDialogs.length - 1] === this.dialog) {
|
|
156
|
-
this.close();
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// Close on click outside
|
|
162
|
-
this.dialog.addEventListener("click", (e) => {
|
|
163
|
-
if (!this.dialog) return;
|
|
164
|
-
const dialogDimensions = this.dialog.getBoundingClientRect();
|
|
165
|
-
const clickedInDialog =
|
|
166
|
-
e.clientX >= dialogDimensions.left &&
|
|
167
|
-
e.clientX <= dialogDimensions.right &&
|
|
168
|
-
e.clientY >= dialogDimensions.top &&
|
|
169
|
-
e.clientY <= dialogDimensions.bottom;
|
|
170
|
-
|
|
171
|
-
if (!clickedInDialog) {
|
|
172
|
-
// Only close if this is the topmost dialog
|
|
173
|
-
const openDialogs = document.querySelectorAll("dialog[open]");
|
|
174
|
-
if (openDialogs.length > 0 && openDialogs[openDialogs.length - 1] === this.dialog) {
|
|
175
|
-
this.close();
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Handle escape key
|
|
181
|
-
this.dialog.addEventListener("keydown", (e) => {
|
|
182
|
-
if (e.key === "Escape") {
|
|
183
|
-
// prevent default dialog closing behavior so we can add closing animation
|
|
184
|
-
e.preventDefault();
|
|
185
|
-
// Only close if this is the topmost dialog
|
|
186
|
-
const openDialogs = document.querySelectorAll("dialog[open]");
|
|
187
|
-
if (openDialogs.length > 0 && openDialogs[openDialogs.length - 1] === this.dialog) {
|
|
188
|
-
this.close();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
// Intercept form submissions to handle dialog close
|
|
194
|
-
const forms = this.dialog.querySelectorAll("form");
|
|
195
|
-
forms.forEach((form) => {
|
|
196
|
-
form.addEventListener("submit", (e) => {
|
|
197
|
-
/**
|
|
198
|
-
* Default form.method = "dialog" submissions cause the dialog to close
|
|
199
|
-
* Default form.method = "post" submissions do not close the dialog
|
|
200
|
-
* Here we intercept the form submission and manage the dialog closing if method = "dialog"
|
|
201
|
-
* so we can add closing animation
|
|
202
|
-
* Normal form event listeners for "submit" will still get the form data
|
|
203
|
-
*/
|
|
204
|
-
if (form.method === "dialog") {
|
|
205
|
-
e.preventDefault();
|
|
206
|
-
// Only close if this is the topmost dialog
|
|
207
|
-
const openDialogs = document.querySelectorAll("dialog[open]");
|
|
208
|
-
if (openDialogs.length > 0 && openDialogs[openDialogs.length - 1] === this.dialog) {
|
|
209
|
-
this.close();
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
private open(): void {
|
|
217
|
-
if (!this.dialog || !this.backdrop) return;
|
|
218
|
-
this.dialog.showModal();
|
|
219
|
-
document.body.classList.add("overflow-hidden");
|
|
220
|
-
this.backdrop.classList.remove("hidden");
|
|
221
|
-
this.backdrop.dataset.state = "open";
|
|
222
|
-
this.dialog.dataset.state = "open";
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
private close(): void {
|
|
226
|
-
if (!this.dialog || !this.backdrop) return;
|
|
227
|
-
this.dialog.dataset.state = "closed";
|
|
228
|
-
this.backdrop.dataset.state = "closed";
|
|
229
|
-
|
|
230
|
-
// Wait for animation to finish before hiding backdrop
|
|
231
|
-
setTimeout(() => {
|
|
232
|
-
this.backdrop?.classList.add("hidden");
|
|
233
|
-
this.dialog?.close();
|
|
234
|
-
const stillOpen = document.querySelectorAll("dialog[open]").length;
|
|
235
|
-
if (stillOpen === 0) {
|
|
236
|
-
document.body.classList.remove("overflow-hidden");
|
|
237
|
-
}
|
|
238
|
-
}, this.animationDuration);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Store instances in a WeakMap to avoid memory leaks
|
|
243
|
-
const dialogInstances = new WeakMap<HTMLElement, DialogHandler>();
|
|
244
|
-
|
|
245
|
-
// Initialize all dialogs
|
|
246
|
-
const setupDialogs = () => {
|
|
247
|
-
document.querySelectorAll(".starwind-dialog").forEach((dialogWrapper, idx) => {
|
|
248
|
-
const wrapper = dialogWrapper as HTMLElement;
|
|
249
|
-
if (!dialogInstances.has(wrapper)) {
|
|
250
|
-
dialogInstances.set(wrapper, new DialogHandler(wrapper, idx));
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
setupDialogs();
|
|
256
|
-
document.addEventListener("astro:after-swap", setupDialogs);
|
|
257
|
-
</script>
|
|
258
|
-
|
|
259
|
-
<style>
|
|
260
|
-
.overflow-hidden {
|
|
261
|
-
overflow: hidden;
|
|
262
|
-
}
|
|
263
|
-
</style>
|
|
@@ -1,35 +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 instead of a button
|
|
7
|
-
*/
|
|
8
|
-
asChild?: boolean;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const { class: className, asChild = false, ...rest } = Astro.props;
|
|
12
|
-
|
|
13
|
-
// Get the first child element if asChild is true
|
|
14
|
-
let hasChildren = false;
|
|
15
|
-
if (Astro.slots.has("default")) {
|
|
16
|
-
hasChildren = true;
|
|
17
|
-
}
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
{
|
|
21
|
-
asChild && hasChildren ? (
|
|
22
|
-
<div class="starwind-dialog-close" data-slot="dialog-close" data-as-child>
|
|
23
|
-
<slot />
|
|
24
|
-
</div>
|
|
25
|
-
) : (
|
|
26
|
-
<button
|
|
27
|
-
type="button"
|
|
28
|
-
class:list={["starwind-dialog-close", className]}
|
|
29
|
-
data-slot="dialog-close"
|
|
30
|
-
{...rest}
|
|
31
|
-
>
|
|
32
|
-
<slot>Demo close button</slot>
|
|
33
|
-
</button>
|
|
34
|
-
)
|
|
35
|
-
}
|