@starwind-ui/core 1.12.4 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/index.js +16 -12
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/components/accordion/Accordion.astro +10 -3
  4. package/dist/src/components/alert/AlertTitle.astro +1 -1
  5. package/dist/src/components/alert-dialog/AlertDialog.astro +4 -2
  6. package/dist/src/components/alert-dialog/AlertDialogTitle.astro +1 -1
  7. package/dist/src/components/badge/Badge.astro +1 -3
  8. package/dist/src/components/button/Button.astro +2 -1
  9. package/dist/src/components/dialog/Dialog.astro +101 -9
  10. package/dist/src/components/dialog/DialogContent.astro +13 -2
  11. package/dist/src/components/dialog/DialogTitle.astro +3 -1
  12. package/dist/src/components/dropdown/Dropdown.astro +4 -2
  13. package/dist/src/components/dropzone/Dropzone.astro +5 -3
  14. package/dist/src/components/image/Image.astro +24 -0
  15. package/dist/src/components/image/index.ts +9 -0
  16. package/dist/src/components/progress/Progress.astro +1 -0
  17. package/dist/src/components/radio-group/RadioGroup.astro +7 -2
  18. package/dist/src/components/select/Select.astro +7 -2
  19. package/dist/src/components/sheet/SheetTitle.astro +1 -1
  20. package/dist/src/components/slider/Slider.astro +411 -0
  21. package/dist/src/components/slider/index.ts +9 -0
  22. package/dist/src/components/switch/Switch.astro +1 -0
  23. package/dist/src/components/tabs/Tabs.astro +4 -2
  24. package/dist/src/components/toast/ToastDescription.astro +21 -0
  25. package/dist/src/components/toast/ToastItem.astro +54 -0
  26. package/dist/src/components/toast/ToastTemplate.astro +25 -0
  27. package/dist/src/components/toast/ToastTitle.astro +57 -0
  28. package/dist/src/components/toast/Toaster.astro +982 -0
  29. package/dist/src/components/toast/index.ts +29 -0
  30. package/dist/src/components/toast/toast-manager.ts +216 -0
  31. package/dist/src/components/toggle/Toggle.astro +4 -2
  32. package/dist/src/components/tooltip/Tooltip.astro +4 -2
  33. package/dist/src/components/tooltip/TooltipContent.astro +1 -1
  34. package/dist/src/components/video/Video.astro +120 -0
  35. package/dist/src/components/video/index.ts +9 -0
  36. package/package.json +5 -3
@@ -0,0 +1,29 @@
1
+ import type { PromiseOptions, PromiseStateOption, ToastOptions, Variant } from "./toast-manager";
2
+ import { toast } from "./toast-manager";
3
+ import ToastDescription from "./ToastDescription.astro";
4
+ import Toaster from "./Toaster.astro";
5
+ import ToastItem from "./ToastItem.astro";
6
+ import ToastTemplate from "./ToastTemplate.astro";
7
+ import ToastTitle from "./ToastTitle.astro";
8
+
9
+ export {
10
+ type PromiseOptions,
11
+ type PromiseStateOption,
12
+ toast,
13
+ ToastDescription,
14
+ Toaster,
15
+ ToastItem,
16
+ type ToastOptions,
17
+ ToastTemplate,
18
+ ToastTitle,
19
+ type Variant,
20
+ };
21
+
22
+ export default {
23
+ Manager: toast,
24
+ Viewport: Toaster,
25
+ Item: ToastItem,
26
+ Title: ToastTitle,
27
+ Description: ToastDescription,
28
+ Template: ToastTemplate,
29
+ };
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Starwind Toast API
3
+ *
4
+ * A simple, framework-agnostic toast notification system.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { toast } from "@/components/starwind/toast";
9
+ *
10
+ * // Simple usage
11
+ * toast("Hello world");
12
+ * toast({ title: "Success", description: "Item saved" });
13
+ *
14
+ * // Variant shortcuts
15
+ * toast.success("Saved successfully");
16
+ * toast.error("Something went wrong");
17
+ * toast.warning("Please check your input");
18
+ * toast.info("New update available");
19
+ * toast.loading("Processing...");
20
+ *
21
+ * // Promise handling
22
+ * toast.promise(saveData(), {
23
+ * loading: "Saving...",
24
+ * success: "Saved!",
25
+ * error: "Failed to save",
26
+ * });
27
+ *
28
+ * // Management
29
+ * const id = toast("Processing...");
30
+ * toast.update(id, { title: "Almost done..." });
31
+ * toast.dismiss(id);
32
+ * toast.dismiss(); // dismiss all
33
+ * ```
34
+ */
35
+
36
+ export type Variant = "default" | "success" | "error" | "warning" | "info" | "loading";
37
+
38
+ export interface ToastOptions {
39
+ id?: string;
40
+ title?: string;
41
+ description?: string;
42
+ variant?: Variant;
43
+ /** Duration in ms. Set to 0 for infinite (no auto-dismiss). */
44
+ duration?: number;
45
+ /** Callback when toast close animation starts */
46
+ onClose?: () => void;
47
+ /** Callback when toast is removed from DOM */
48
+ onRemove?: () => void;
49
+ action?: {
50
+ label: string;
51
+ onClick: () => void;
52
+ };
53
+ }
54
+
55
+ export interface PromiseStateOption {
56
+ title?: string;
57
+ description?: string;
58
+ duration?: number;
59
+ }
60
+
61
+ export type PromiseStateValue<T> =
62
+ | string
63
+ | PromiseStateOption
64
+ | ((data: T) => string | PromiseStateOption);
65
+
66
+ export interface PromiseOptions<T, E = Error> {
67
+ loading: string | PromiseStateOption;
68
+ success: PromiseStateValue<T>;
69
+ error: PromiseStateValue<E>;
70
+ }
71
+
72
+ interface ToastManager {
73
+ add(options: ToastOptions): string;
74
+ update(id: string, options: Partial<ToastOptions>): void;
75
+ close(id: string): void;
76
+ closeAll(): void;
77
+ }
78
+
79
+ /**
80
+ * Get the toast manager instance from the window
81
+ */
82
+ function getManager(): ToastManager | null {
83
+ if (typeof window === "undefined") return null;
84
+ return (window as any).__starwind__.toast as ToastManager | null;
85
+ }
86
+
87
+ /**
88
+ * Normalize a string or options object to ToastOptions
89
+ */
90
+ function normalizeOption<T>(
91
+ value: string | PromiseStateOption | ((data: T) => string | PromiseStateOption),
92
+ data?: T,
93
+ ): Omit<ToastOptions, "variant"> {
94
+ const resolved = typeof value === "function" ? value(data as T) : value;
95
+ if (typeof resolved === "string") {
96
+ return { title: resolved };
97
+ }
98
+ return resolved;
99
+ }
100
+
101
+ /**
102
+ * Create a toast notification
103
+ */
104
+ function createToast(
105
+ messageOrOptions: string | ToastOptions,
106
+ extraOptions?: Omit<ToastOptions, "title">,
107
+ ): string {
108
+ let options: ToastOptions;
109
+ if (typeof messageOrOptions === "string") {
110
+ options = { title: messageOrOptions, ...extraOptions };
111
+ } else {
112
+ options = messageOrOptions;
113
+ }
114
+
115
+ const manager = getManager();
116
+ if (manager) {
117
+ return manager.add(options);
118
+ }
119
+
120
+ console.warn("Toast: No Toaster found. Add <Toaster /> to your layout.");
121
+ return "";
122
+ }
123
+
124
+ /**
125
+ * Create a toast with a specific variant
126
+ */
127
+ function createVariantToast(
128
+ variant: Variant,
129
+ message: string,
130
+ options?: Omit<ToastOptions, "variant">,
131
+ ): string {
132
+ return createToast({ ...options, title: message, variant });
133
+ }
134
+
135
+ /**
136
+ * Toast API interface
137
+ */
138
+ interface ToastAPI {
139
+ (message: string, options?: Omit<ToastOptions, "title">): string;
140
+ (options: ToastOptions): string;
141
+ success(message: string, options?: Omit<ToastOptions, "variant">): string;
142
+ error(message: string, options?: Omit<ToastOptions, "variant">): string;
143
+ warning(message: string, options?: Omit<ToastOptions, "variant">): string;
144
+ info(message: string, options?: Omit<ToastOptions, "variant">): string;
145
+ loading(message: string, options?: Omit<ToastOptions, "variant">): string;
146
+ promise<T, E = Error>(promise: Promise<T>, options: PromiseOptions<T, E>): Promise<T>;
147
+ update(id: string, options: Partial<ToastOptions>): void;
148
+ dismiss(id?: string): void;
149
+ }
150
+
151
+ /**
152
+ * Main toast function with variant methods attached
153
+ */
154
+ const toast = createToast as ToastAPI;
155
+
156
+ toast.success = (message: string, options?: Omit<ToastOptions, "variant">) =>
157
+ createVariantToast("success", message, options);
158
+
159
+ toast.error = (message: string, options?: Omit<ToastOptions, "variant">) =>
160
+ createVariantToast("error", message, options);
161
+
162
+ toast.warning = (message: string, options?: Omit<ToastOptions, "variant">) =>
163
+ createVariantToast("warning", message, options);
164
+
165
+ toast.info = (message: string, options?: Omit<ToastOptions, "variant">) =>
166
+ createVariantToast("info", message, options);
167
+
168
+ toast.loading = (message: string, options?: Omit<ToastOptions, "variant">) =>
169
+ createVariantToast("loading", message, { ...options, duration: 0 });
170
+
171
+ toast.promise = async <T, E = Error>(
172
+ promise: Promise<T>,
173
+ options: PromiseOptions<T, E>,
174
+ ): Promise<T> => {
175
+ const loadingOpts = normalizeOption(options.loading);
176
+ const id = createToast({
177
+ ...loadingOpts,
178
+ variant: "loading",
179
+ duration: 0, // Don't auto-dismiss while loading
180
+ });
181
+
182
+ try {
183
+ const data = await promise;
184
+ const successOpts = normalizeOption(options.success, data);
185
+ toast.update(id, { ...successOpts, variant: "success" });
186
+ return data;
187
+ } catch (error) {
188
+ const errorOpts = normalizeOption(options.error, error as E);
189
+ toast.update(id, { ...errorOpts, variant: "error" });
190
+ throw error;
191
+ }
192
+ };
193
+
194
+ toast.update = (id: string, options: Partial<ToastOptions>): void => {
195
+ const manager = getManager();
196
+ if (manager) {
197
+ manager.update(id, options);
198
+ } else {
199
+ console.warn("Toast: No Toaster found. Add <Toaster /> to your layout.");
200
+ }
201
+ };
202
+
203
+ toast.dismiss = (id?: string): void => {
204
+ const manager = getManager();
205
+ if (!manager) {
206
+ // Can't dismiss if manager isn't ready
207
+ return;
208
+ }
209
+ if (id) {
210
+ manager.close(id);
211
+ } else {
212
+ manager.closeAll();
213
+ }
214
+ };
215
+
216
+ export { toast };
@@ -158,15 +158,17 @@ const dataState = defaultPressed ? "on" : "off";
158
158
 
159
159
  // Store instances in a WeakMap to avoid memory leaks
160
160
  const toggleInstances = new WeakMap<HTMLElement, ToggleHandler>();
161
+ let toggleCounter = 0;
161
162
 
162
163
  const setupToggles = () => {
163
- document.querySelectorAll<HTMLButtonElement>(".starwind-toggle").forEach((toggle, idx) => {
164
+ document.querySelectorAll<HTMLButtonElement>(".starwind-toggle").forEach((toggle) => {
164
165
  if (!toggleInstances.has(toggle)) {
165
- toggleInstances.set(toggle, new ToggleHandler(toggle, idx));
166
+ toggleInstances.set(toggle, new ToggleHandler(toggle, toggleCounter++));
166
167
  }
167
168
  });
168
169
  };
169
170
 
170
171
  setupToggles();
171
172
  document.addEventListener("astro:after-swap", setupToggles);
173
+ document.addEventListener("starwind:init", setupToggles);
172
174
  </script>
@@ -223,15 +223,17 @@ const {
223
223
 
224
224
  // Store instances in a WeakMap to avoid memory leaks
225
225
  const tooltipInstances = new WeakMap<HTMLElement, TooltipHandler>();
226
+ let tooltipCounter = 0;
226
227
 
227
228
  const setupTooltips = () => {
228
- document.querySelectorAll<HTMLElement>(".starwind-tooltip").forEach((tooltip, idx) => {
229
+ document.querySelectorAll<HTMLElement>(".starwind-tooltip").forEach((tooltip) => {
229
230
  if (!tooltipInstances.has(tooltip)) {
230
- tooltipInstances.set(tooltip, new TooltipHandler(tooltip, idx));
231
+ tooltipInstances.set(tooltip, new TooltipHandler(tooltip, tooltipCounter++));
231
232
  }
232
233
  });
233
234
  };
234
235
 
235
236
  setupTooltips();
236
237
  document.addEventListener("astro:after-swap", setupTooltips);
238
+ document.addEventListener("starwind:init", setupTooltips);
237
239
  </script>
@@ -34,7 +34,7 @@ type Props = HTMLAttributes<"div"> & {
34
34
  export const tooltipContent = tv({
35
35
  base: [
36
36
  "starwind-tooltip-content",
37
- "absolute z-50 hidden px-3 py-1.5 whitespace-nowrap shadow-xs will-change-transform",
37
+ "absolute z-50 hidden w-fit px-3 py-1.5 will-change-transform",
38
38
  "bg-foreground text-background rounded-md",
39
39
  "animate-in fade-in zoom-in-95",
40
40
  "data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fade-out zoom-out-95",
@@ -0,0 +1,120 @@
1
+ ---
2
+ import type { HTMLAttributes } from "astro/types";
3
+ import { tv } from "tailwind-variants";
4
+
5
+ export const video = tv({
6
+ base: "starwind-video aspect-video h-auto w-full",
7
+ });
8
+
9
+ type Props = HTMLAttributes<"video"> &
10
+ HTMLAttributes<"iframe"> & {
11
+ src: string;
12
+ title?: string;
13
+ autoplay?: boolean;
14
+ muted?: boolean;
15
+ loop?: boolean;
16
+ controls?: boolean;
17
+ poster?: string;
18
+ };
19
+
20
+ const {
21
+ class: className,
22
+ src,
23
+ title = "Video",
24
+ autoplay = false,
25
+ muted = false,
26
+ loop = false,
27
+ controls = true,
28
+ poster,
29
+ ...rest
30
+ } = Astro.props;
31
+
32
+ /**
33
+ * Detects the video type from the src URL
34
+ */
35
+ function getVideoType(url: string): "youtube" | "youtube-shorts" | "native" {
36
+ if (url.includes("youtube.com/shorts/") || url.includes("youtu.be/shorts/")) {
37
+ return "youtube-shorts";
38
+ }
39
+ if (
40
+ url.includes("youtube.com") ||
41
+ url.includes("youtu.be") ||
42
+ url.includes("youtube-nocookie.com")
43
+ ) {
44
+ return "youtube";
45
+ }
46
+ return "native";
47
+ }
48
+
49
+ /**
50
+ * Extracts YouTube video ID from various URL formats
51
+ */
52
+ function getYouTubeId(url: string): string | null {
53
+ const patterns = [
54
+ /youtube\.com\/shorts\/([^?&]+)/,
55
+ /youtube\.com\/watch\?v=([^&]+)/,
56
+ /youtube\.com\/embed\/([^?&]+)/,
57
+ /youtu\.be\/([^?&]+)/,
58
+ /youtube-nocookie\.com\/embed\/([^?&]+)/,
59
+ ];
60
+
61
+ for (const pattern of patterns) {
62
+ const match = url.match(pattern);
63
+ if (match) return match[1];
64
+ }
65
+ return null;
66
+ }
67
+
68
+ /**
69
+ * Builds YouTube embed URL with parameters
70
+ */
71
+ function buildYouTubeEmbedUrl(videoId: string, isShort: boolean): string {
72
+ const params = new URLSearchParams();
73
+ if (autoplay) params.set("autoplay", "1");
74
+ if (muted) params.set("mute", "1");
75
+ if (loop) {
76
+ params.set("loop", "1");
77
+ params.set("playlist", videoId);
78
+ }
79
+ if (!controls) params.set("controls", "0");
80
+
81
+ const baseUrl = `https://www.youtube-nocookie.com/embed/${videoId}`;
82
+ const queryString = params.toString();
83
+ return queryString ? `${baseUrl}?${queryString}` : baseUrl;
84
+ }
85
+
86
+ const videoType = getVideoType(src);
87
+ const youtubeId = videoType !== "native" ? getYouTubeId(src) : null;
88
+ const isShort = videoType === "youtube-shorts";
89
+ const embedUrl = youtubeId ? buildYouTubeEmbedUrl(youtubeId, isShort) : null;
90
+ ---
91
+
92
+ {
93
+ videoType === "native" || !embedUrl ? (
94
+ <video
95
+ class={video({ class: className })}
96
+ src={src}
97
+ autoplay={autoplay}
98
+ muted={muted}
99
+ loop={loop}
100
+ controls={controls}
101
+ poster={poster}
102
+ data-slot="video"
103
+ {...rest}
104
+ >
105
+ <track kind="captions" />
106
+ </video>
107
+ ) : (
108
+ <iframe
109
+ class={video({ class: className })}
110
+ src={embedUrl}
111
+ title={title}
112
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
113
+ referrerpolicy="strict-origin-when-cross-origin"
114
+ allowfullscreen
115
+ data-slot="video"
116
+ data-video-type={isShort ? "youtube-shorts" : "youtube"}
117
+ {...rest}
118
+ />
119
+ )
120
+ }
@@ -0,0 +1,9 @@
1
+ import Video, { video } from "./Video.astro";
2
+
3
+ const VideoVariants = {
4
+ video,
5
+ };
6
+
7
+ export { Video, VideoVariants };
8
+
9
+ export default Video;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starwind-ui/core",
3
- "version": "1.12.4",
3
+ "version": "1.14.0",
4
4
  "description": "Starwind UI core components and registry",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -29,8 +29,8 @@
29
29
  ],
30
30
  "devDependencies": {
31
31
  "astro": "^5.11.0",
32
- "fs-extra": "^11.3.0",
33
- "glob": "^11.0.3",
32
+ "fs-extra": "^11.3.3",
33
+ "glob": "^13.0.0",
34
34
  "tailwindcss": "^4.1.11",
35
35
  "tsup": "^8.5.0"
36
36
  },
@@ -40,6 +40,8 @@
40
40
  "dev": "tsup --watch",
41
41
  "core:link": "pnpm link --global",
42
42
  "core:unlink": "pnpm rm --global @starwind-ui/core",
43
+ "core:yalc:link": "yalc publish",
44
+ "core:yalc:unlink": "yalc remove @starwind-ui/core",
43
45
  "publish:beta": "pnpm publish --tag beta --access public",
44
46
  "publish:next": "pnpm publish --tag next --access public",
45
47
  "publish:release": "pnpm publish --access public"