vaderjs-daisyui 0.0.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.
Files changed (28) hide show
  1. package/Components/Actions/Button/index.tsx +63 -0
  2. package/Components/Actions/Dropdown/index.tsx +104 -0
  3. package/Components/Actions/Fab/index.tsx +76 -0
  4. package/Components/Actions/Modal/index.tsx +147 -0
  5. package/Components/Actions/Swap/index.tsx +52 -0
  6. package/Components/Actions/ThemeController/index.tsx +133 -0
  7. package/Components/Data/Display/Accordion/index.tsx +82 -0
  8. package/Components/Data/Display/Avatar/index.tsx +96 -0
  9. package/Components/Data/Display/Badge/index.tsx +46 -0
  10. package/Components/Data/Display/Card/index.tsx +72 -0
  11. package/Components/Data/Display/Carousel/index.tsx +72 -0
  12. package/Components/Data/Display/ChatBubble/index.tsx +57 -0
  13. package/Components/Data/Display/Collapse/index.tsx +60 -0
  14. package/Components/Data/Display/Countdown/index.tsx +97 -0
  15. package/Components/Data/Display/Diff/index.tsx +60 -0
  16. package/Components/Data/Display/Hover/Card/index.tsx +37 -0
  17. package/Components/Data/Display/Hover/Gallery/index.tsx +57 -0
  18. package/Components/Data/Display/Keyboard/index.tsx +31 -0
  19. package/Components/Data/Display/List/index.tsx +93 -0
  20. package/Components/Data/Display/Stat/index.tsx +114 -0
  21. package/Components/Data/Display/Table/index.tsx +33 -0
  22. package/Components/Data/Display/TextRotate/index.tsx +118 -0
  23. package/Components/Data/Display/Timeline/index.tsx +209 -0
  24. package/Components/Navigation/BreadCrumbs/index.tsx +201 -0
  25. package/Components/Navigation/Doc/index.tsx +394 -0
  26. package/Components/Navigation/Link/index.tsx +87 -0
  27. package/index.ts +130 -0
  28. package/package.json +15 -0
@@ -0,0 +1,96 @@
1
+ import { component, createElement, VNode } from "vaderjs";
2
+
3
+ export type AvatarProps = {
4
+ src?: string;
5
+ size?: string; // w-8, w-12, w-16, w-24, etc.
6
+ rounded?: "none" | "sm" | "md" | "xl" | "full";
7
+ mask?: string; // mask-heart, mask-squircle, mask-hexagon-2
8
+ ring?: string; // ring-primary, ring-secondary, etc.
9
+ ringOffset?: string; // ring-offset-base-100, etc.
10
+ online?: boolean;
11
+ offline?: boolean;
12
+ placeholder?: string;
13
+ className?: string;
14
+ };
15
+
16
+ export type AvatarGroupProps = {
17
+ children: VNode[];
18
+ counter?: number; // show "+N" on last avatar
19
+ space?: string; // spacing e.g. -space-x-6
20
+ className?: string;
21
+ };
22
+
23
+ export const Avatar = component((props: AvatarProps) => {
24
+ const {
25
+ src,
26
+ size = "w-12",
27
+ rounded = "full",
28
+ mask,
29
+ ring,
30
+ ringOffset,
31
+ online,
32
+ offline,
33
+ placeholder,
34
+ className,
35
+ } = props;
36
+
37
+ const containerClasses = ["avatar"];
38
+ if (online) containerClasses.push("avatar-online");
39
+ if (offline) containerClasses.push("avatar-offline");
40
+ if (placeholder) containerClasses.push("avatar-placeholder");
41
+ if (className) containerClasses.push(className);
42
+
43
+ const avatarClasses = [
44
+ size,
45
+ rounded === "none" ? "" : `rounded-${rounded}`,
46
+ mask ? `mask ${mask}` : "",
47
+ ring ? `ring-2 ${ring}` : "",
48
+ ringOffset ? `ring-offset-2 ${ringOffset}` : "",
49
+ ].filter(Boolean).join(" ");
50
+
51
+ const fontSize = size.includes("w-24")
52
+ ? "text-3xl"
53
+ : size.includes("w-16")
54
+ ? "text-xl"
55
+ : size.includes("w-12")
56
+ ? "text-base"
57
+ : "text-xs";
58
+
59
+ return createElement(
60
+ "div",
61
+ { className: containerClasses.join(" ") },
62
+ createElement(
63
+ "div",
64
+ { className: avatarClasses },
65
+ placeholder
66
+ ? createElement("span", { className: fontSize }, placeholder)
67
+ : src
68
+ ? createElement("img", { src })
69
+ : null
70
+ )
71
+ );
72
+ });
73
+
74
+ export const AvatarGroup = component((props: AvatarGroupProps) => {
75
+ const { children, counter, space = "-space-x-6", className } = props;
76
+ const groupClasses = ["avatar-group", space];
77
+ if (className) groupClasses.push(className);
78
+
79
+ const renderedChildren = [...children];
80
+ if (counter) {
81
+ // Add last avatar as counter
82
+ renderedChildren.push(
83
+ createElement(
84
+ "div",
85
+ { className: "avatar avatar-placeholder" },
86
+ createElement(
87
+ "div",
88
+ { className: `bg-neutral text-neutral-content ${children[0]?.props?.size || "w-12"} rounded-full` },
89
+ `+${counter}`
90
+ )
91
+ )
92
+ );
93
+ }
94
+
95
+ return createElement("div", { className: groupClasses.join(" ") }, ...renderedChildren);
96
+ });
@@ -0,0 +1,46 @@
1
+ import { component, createElement, VNode } from "vaderjs";
2
+
3
+ export type BadgeProps = {
4
+ children?: string | VNode;
5
+ size?: "xs" | "sm" | "md" | "lg" | "xl";
6
+ color?: "neutral" | "primary" | "secondary" | "accent" | "info" | "success" | "warning" | "error";
7
+ style?: "soft" | "outline" | "dash" | "ghost";
8
+ icon?: VNode; // optional SVG icon
9
+ className?: string;
10
+ };
11
+
12
+ export const Badge = component((props: BadgeProps) => {
13
+ const { children, size = "md", color = "neutral", style, icon, className } = props;
14
+
15
+ const classes = ["badge"];
16
+ if (size) classes.push(`badge-${size}`);
17
+ if (color) classes.push(`badge-${color}`);
18
+ if (style) classes.push(`badge-${style}`);
19
+ if (className) classes.push(className);
20
+
21
+ return createElement(
22
+ "span",
23
+ { className: classes.join(" ") },
24
+ icon ? createElement("span", { className: "badge-icon mr-1" }, icon) : null,
25
+ children
26
+ );
27
+ });
28
+
29
+ export type BadgeButtonProps = {
30
+ label: string | VNode;
31
+ badge?: BadgeProps;
32
+ className?: string;
33
+ };
34
+
35
+ export const BadgeButton = component((props: BadgeButtonProps) => {
36
+ const { label, badge, className } = props;
37
+ const btnClasses = ["btn"];
38
+ if (className) btnClasses.push(className);
39
+
40
+ return createElement(
41
+ "button",
42
+ { className: btnClasses.join(" ") },
43
+ label,
44
+ badge ? createElement(Badge, badge) : null
45
+ );
46
+ });
@@ -0,0 +1,72 @@
1
+ import { component, createElement, VNode } from "vaderjs";
2
+ import { Badge, BadgeProps } from "../Badge";
3
+
4
+ export type CardProps = {
5
+ title?: string | VNode;
6
+ body?: string | VNode;
7
+ actions?: VNode | VNode[];
8
+ figure?: VNode;
9
+ size?: "xs" | "sm" | "md" | "lg" | "xl";
10
+ style?: "border" | "dash";
11
+ side?: boolean; // card-side
12
+ imageFull?: boolean; // image-full
13
+ center?: boolean; // center content
14
+ className?: string;
15
+ };
16
+
17
+ export const Card = component((props: CardProps) => {
18
+ const {
19
+ title,
20
+ body,
21
+ actions,
22
+ figure,
23
+ size = "md",
24
+ style,
25
+ side,
26
+ imageFull,
27
+ center,
28
+ className,
29
+ } = props;
30
+
31
+ const classes = ["card"];
32
+ if (size) classes.push(`card-${size}`);
33
+ if (style === "border") classes.push("card-border");
34
+ if (style === "dash") classes.push("card-dash");
35
+ if (side) classes.push("card-side");
36
+ if (imageFull) classes.push("image-full");
37
+ if (className) classes.push(className);
38
+
39
+ const bodyClasses = ["card-body"];
40
+ if (center) bodyClasses.push("items-center", "text-center");
41
+
42
+ return createElement(
43
+ "div",
44
+ { className: classes.join(" ") },
45
+ figure ? figure : null,
46
+ createElement(
47
+ "div",
48
+ { className: bodyClasses.join(" ") },
49
+ title ? (typeof title === "string" ? createElement("h2", { className: "card-title" }, title) : title) : null,
50
+ body ? (typeof body === "string" ? createElement("p", {}, body) : body) : null,
51
+ actions ? (Array.isArray(actions) ? actions.map(a => a) : actions) : null
52
+ )
53
+ );
54
+ });
55
+
56
+ // Optional helper for card with badge on title
57
+ export type CardWithBadgeProps = CardProps & {
58
+ badges?: BadgeProps[];
59
+ };
60
+
61
+ export const CardWithBadge = component((props: CardWithBadgeProps) => {
62
+ const { badges, title, ...cardProps } = props;
63
+
64
+ const titleVNode = createElement(
65
+ "h2",
66
+ { className: "card-title flex items-center gap-2" },
67
+ title,
68
+ badges ? badges.map(b => createElement(Badge, b)) : null
69
+ );
70
+
71
+ return createElement(Card, { ...cardProps, title: titleVNode });
72
+ });
@@ -0,0 +1,72 @@
1
+ // Carousel.tsx
2
+ import { component, createElement } from "vaderjs";
3
+
4
+ interface CarouselProps {
5
+ images: string[];
6
+ snap?: "start" | "center" | "end"; // snap position
7
+ width?: "full" | "half"; // width of items
8
+ vertical?: boolean; // vertical layout
9
+ containerWidth?: string; // optional width of carousel container, e.g., "w-96"
10
+ fullBleed?: boolean; // full-bleed layout
11
+ indicators?: boolean; // show indicator buttons
12
+ controls?: boolean; // show next/prev buttons
13
+ }
14
+
15
+ const Carousel = component(({
16
+ images,
17
+ snap = "start",
18
+ width = "full",
19
+ vertical = false,
20
+ containerWidth = "w-64",
21
+ fullBleed = false,
22
+ indicators = false,
23
+ controls = false,
24
+ }: CarouselProps) => {
25
+ const snapClass = snap === "center" ? "carousel-center" : snap === "end" ? "carousel-end" : "carousel-start";
26
+ const orientationClass = vertical ? "carousel-vertical" : "";
27
+ const itemClass = width === "full" ? "w-full" : "w-1/2";
28
+ const containerClass = fullBleed
29
+ ? "carousel carousel-center bg-neutral rounded-box max-w-md space-x-4 p-4"
30
+ : `carousel ${snapClass} ${orientationClass} ${containerWidth} rounded-box`;
31
+
32
+ const len = images.length;
33
+
34
+ // VaderJS requires returning VNode or VNode[], no fragments <>
35
+ const carouselItems = images.map((src, i) => {
36
+ const prev = (i - 1 + len) % len;
37
+ const next = (i + 1) % len;
38
+
39
+ return createElement("div", {
40
+ key: i,
41
+ id: controls ? `slide${i + 1}` : indicators ? `item${i + 1}` : undefined,
42
+ class: `carousel-item ${itemClass} ${vertical ? "h-full" : ""} relative`
43
+ },
44
+ createElement("img", {
45
+ src,
46
+ class: fullBleed ? "rounded-box" : "w-full",
47
+ alt: `Carousel ${i + 1}`
48
+ }),
49
+ controls ? createElement("div", {
50
+ class: "absolute left-5 right-5 top-1/2 flex -translate-y-1/2 transform justify-between"
51
+ },
52
+ createElement("a", { href: `#slide${prev + 1}`, class: "btn btn-circle" }, "❮"),
53
+ createElement("a", { href: `#slide${next + 1}`, class: "btn btn-circle" }, "❯")
54
+ ) : null
55
+ );
56
+ });
57
+
58
+ const indicatorButtons = indicators && !controls
59
+ ? createElement("div", { class: "flex justify-center w-full py-2 gap-2" },
60
+ images.map((_, i) =>
61
+ createElement("a", { href: `#item${i + 1}`, class: "btn btn-xs", key: i }, `${i + 1}`)
62
+ )
63
+ )
64
+ : null;
65
+
66
+ return [
67
+ createElement("div", { class: containerClass }, ...carouselItems),
68
+ indicatorButtons
69
+ ];
70
+ });
71
+
72
+ export default Carousel;
@@ -0,0 +1,57 @@
1
+ // ChatBubbles.ts
2
+ import { component, createElement } from "vaderjs";
3
+
4
+ export type ChatColor =
5
+ | "neutral"
6
+ | "primary"
7
+ | "secondary"
8
+ | "accent"
9
+ | "info"
10
+ | "success"
11
+ | "warning"
12
+ | "error";
13
+
14
+ export interface ChatMessage {
15
+ id: string | number;
16
+ author?: string;
17
+ avatar?: string;
18
+ time?: string;
19
+ content: string;
20
+ placement?: "start" | "end";
21
+ header?: string;
22
+ footer?: string;
23
+ color?: ChatColor;
24
+ }
25
+
26
+ export interface ChatProps {
27
+ messages: ChatMessage[];
28
+ }
29
+
30
+ const ChatBubbles = component(({ messages }: ChatProps) => {
31
+ const renderMessage = (msg: ChatMessage) => {
32
+ const placementClass = msg.placement === "end" ? "chat-end" : "chat-start";
33
+ const bubbleColor = msg.color ? `chat-bubble-${msg.color}` : "";
34
+
35
+ return createElement("div", { class: `chat ${placementClass}`, key: msg.id }, [
36
+ msg.avatar
37
+ ? createElement("div", { class: "chat-image avatar" },
38
+ createElement("div", { class: "w-10 rounded-full" },
39
+ createElement("img", { src: msg.avatar, alt: msg.author ?? "User" })
40
+ )
41
+ )
42
+ : null,
43
+ msg.header
44
+ ? createElement("div", { class: "chat-header" }, [
45
+ msg.header,
46
+ msg.time ? createElement("time", { class: "text-xs opacity-50" }, msg.time) : null
47
+ ])
48
+ : null,
49
+ createElement("div", { class: `chat-bubble ${bubbleColor}` }, msg.content),
50
+ msg.footer ? createElement("div", { class: "chat-footer opacity-50" }, msg.footer) : null
51
+ ]);
52
+ };
53
+
54
+ return messages.map(renderMessage);
55
+ });
56
+
57
+ export default ChatBubbles;
@@ -0,0 +1,60 @@
1
+ // Collapse.ts
2
+ import { component, createElement } from "vaderjs";
3
+
4
+ export interface CollapseItem {
5
+ id: string | number;
6
+ title: string;
7
+ content: string;
8
+ focus?: boolean; // true = focus-based collapse
9
+ checkbox?: boolean; // true = checkbox-controlled
10
+ details?: boolean; // true = use <details>/<summary>
11
+ open?: boolean; // force open
12
+ close?: boolean; // force close
13
+ arrow?: boolean; // arrow icon
14
+ plus?: boolean; // plus/minus icon
15
+ placementStart?: boolean; // move icon to start
16
+ customClasses?: string; // any additional Tailwind classes
17
+ }
18
+
19
+ export interface CollapseProps {
20
+ items: CollapseItem[];
21
+ }
22
+
23
+ const Collapse = component(({ items }: CollapseProps) => {
24
+ const renderItem = (item: CollapseItem) => {
25
+ let baseClasses = "collapse";
26
+ if (item.focus) baseClasses += " focus:outline-none";
27
+ if (item.arrow) baseClasses += " collapse-arrow";
28
+ if (item.plus) baseClasses += " collapse-plus";
29
+ if (item.open) baseClasses += " collapse-open";
30
+ if (item.close) baseClasses += " collapse-close";
31
+ if (item.customClasses) baseClasses += ` ${item.customClasses}`;
32
+
33
+ const titleClasses = `collapse-title font-semibold${
34
+ item.placementStart ? " after:start-5 after:end-auto pe-4 ps-12" : ""
35
+ }`;
36
+ const contentClasses = "collapse-content text-sm";
37
+
38
+ if (item.details) {
39
+ // Using details/summary
40
+ return createElement(
41
+ "details",
42
+ { class: baseClasses, key: item.id },
43
+ [
44
+ createElement("summary", { class: titleClasses }, item.title),
45
+ createElement("div", { class: contentClasses }, item.content)
46
+ ]
47
+ );
48
+ }
49
+
50
+ return createElement("div", { class: baseClasses, key: item.id, tabindex: item.focus ? 0 : undefined }, [
51
+ item.checkbox ? createElement("input", { type: "checkbox" }) : null,
52
+ createElement("div", { class: titleClasses }, item.title),
53
+ createElement("div", { class: contentClasses }, item.content)
54
+ ]);
55
+ };
56
+
57
+ return items.map(renderItem);
58
+ });
59
+
60
+ export default Collapse;
@@ -0,0 +1,97 @@
1
+ // Countdown.ts
2
+ import { component, createElement, useState, useEffect, useRef } from "vaderjs";
3
+
4
+ export interface CountdownUnit {
5
+ label?: string;
6
+ value: number;
7
+ max?: number; // e.g., 59 for seconds
8
+ digits?: number;
9
+ animation?: string;
10
+ containerClass?: string;
11
+ numberClass?: string;
12
+ }
13
+
14
+ export interface CountdownProps {
15
+ units: CountdownUnit[];
16
+ interval?: number; // ms per tick
17
+ loop?: boolean;
18
+ }
19
+
20
+ const Countdown = component(({ units, interval = 1000, loop = true }: CountdownProps) => {
21
+ const [counts, setCounts] = useState(() => units.map(u => u.value));
22
+
23
+ // Refs to avoid stale closures in setInterval
24
+ const unitsRef = useRef(units);
25
+ const loopRef = useRef(loop);
26
+
27
+ useEffect(() => {
28
+ unitsRef.current = units;
29
+ loopRef.current = loop;
30
+ }, [units, loop]);
31
+
32
+ useEffect(() => {
33
+ const timer = setInterval(() => {
34
+ setCounts(prev => {
35
+ const newCounts = [...prev];
36
+ const currentUnits = unitsRef.current;
37
+ const shouldLoop = loopRef.current;
38
+
39
+ let i = newCounts.length - 1;
40
+ let carry = true;
41
+
42
+ while (i >= 0 && carry) {
43
+ const max = currentUnits[i]?.max ?? (i === newCounts.length - 1 ? 59 : 99);
44
+ if (newCounts[i] > 0) {
45
+ newCounts[i]--;
46
+ carry = false;
47
+ } else {
48
+ newCounts[i] = max;
49
+ i--;
50
+ }
51
+ }
52
+
53
+ if (carry && shouldLoop) {
54
+ // Reset all to initial values if looping
55
+ return currentUnits.map(u => u.value);
56
+ }
57
+
58
+ if (carry && !shouldLoop) {
59
+ // Stop at zero
60
+ clearInterval(timer);
61
+ return newCounts.map(() => 0);
62
+ }
63
+
64
+ return newCounts;
65
+ });
66
+ }, interval);
67
+
68
+ return () => clearInterval(timer);
69
+ }, [interval]);
70
+
71
+ const renderUnit = (val: number, unit: CountdownUnit, idx: number) => {
72
+ const digits = unit.digits ?? 2;
73
+ const padded = String(val).padStart(digits, "0");
74
+
75
+ return createElement(
76
+ "div",
77
+ { key: idx, class: `flex flex-col items-center ${unit.containerClass || ""}` },
78
+ [
79
+ createElement("span", {
80
+ class: `countdown font-mono ${unit.numberClass || ""} ${unit.animation || ""}`,
81
+ style: `--value:${val}; --digits:${digits};`,
82
+ "aria-live": "polite",
83
+ "aria-label": String(val)
84
+ }, padded),
85
+ unit.label ? createElement("span", { class: "text-sm mt-1" }, unit.label) : null
86
+ ]
87
+ );
88
+ };
89
+
90
+ return createElement(
91
+ "div",
92
+ { class: "grid auto-cols-max grid-flow-col gap-5 text-center" },
93
+ counts.map((val, idx) => renderUnit(val, units[idx], idx))
94
+ );
95
+ });
96
+
97
+ export default Countdown;
@@ -0,0 +1,60 @@
1
+ import { component, createElement, useRef, useEffect } from "vaderjs";
2
+
3
+ export interface DiffProps {
4
+ item1: any;
5
+ item2: any;
6
+ className?: string;
7
+ initial?: number; // 0–100
8
+ }
9
+
10
+ const Diff = component(
11
+ ({ item1, item2, className = "diff rounded-field aspect-16/9", initial = 50 }: DiffProps) => {
12
+ const containerRef = useRef<HTMLDivElement>(null);
13
+
14
+ useEffect(() => {
15
+ const el = containerRef.current;
16
+ if (!el) return;
17
+ el.style.setProperty("--pos", `${initial}%`);
18
+ }, [initial]);
19
+
20
+ return createElement(
21
+ "figure",
22
+ {
23
+ ref: containerRef,
24
+ class: className,
25
+ tabindex: 0,
26
+ },
27
+ [
28
+ createElement(
29
+ "div",
30
+ { class: "diff-item-1", role: "img", tabindex: 0 },
31
+ item1
32
+ ),
33
+
34
+ createElement(
35
+ "div",
36
+ { class: "diff-item-2", role: "img" },
37
+ item2
38
+ ),
39
+
40
+ // 🔑 THIS is the key
41
+ createElement("input", {
42
+ type: "range",
43
+ min: 0,
44
+ max: 100,
45
+ value: initial,
46
+ class: "diff-resizer",
47
+ "aria-label": "Resize comparison",
48
+ onInput: (e: any) => {
49
+ containerRef.current?.style.setProperty(
50
+ "--pos",
51
+ `${e.target.value}%`
52
+ );
53
+ },
54
+ }),
55
+ ]
56
+ );
57
+ }
58
+ );
59
+
60
+ export default Diff;
@@ -0,0 +1,37 @@
1
+ import { component, createElement } from "vaderjs";
2
+
3
+ export interface Hover3DProps {
4
+ children: any;
5
+ className?: string;
6
+ as?: "div" | "a";
7
+ href?: string;
8
+ }
9
+
10
+ const Hover3D = component(
11
+ ({ children, className = "", as = "div", href }: Hover3DProps) => {
12
+ const Tag = as;
13
+
14
+ return createElement(
15
+ Tag,
16
+ {
17
+ class: `hover-3d ${className}`.trim(),
18
+ ...(Tag === "a" && href ? { href } : {}),
19
+ },
20
+
21
+ // ✅ IMPORTANT: spread children
22
+ children,
23
+
24
+ // required 8 hover zones
25
+ createElement("div", {}),
26
+ createElement("div", {}),
27
+ createElement("div", {}),
28
+ createElement("div", {}),
29
+ createElement("div", {}),
30
+ createElement("div", {}),
31
+ createElement("div", {}),
32
+ createElement("div", {})
33
+ );
34
+ }
35
+ );
36
+
37
+ export default Hover3D;
@@ -0,0 +1,57 @@
1
+ // HoverGallery.ts
2
+ import { component, createElement } from "vaderjs";
3
+
4
+ export interface HoverGalleryProps {
5
+ images: (string | JSX.Element)[];
6
+ className?: string;
7
+ as?: "figure" | "div";
8
+ altPrefix?: string;
9
+ }
10
+
11
+ /**
12
+ * HoverGallery
13
+ * DaisyUI hover-gallery component
14
+ *
15
+ * Rules:
16
+ * - Max 10 images (CSS limitation)
17
+ * - First image is default
18
+ * - Remaining images activate on horizontal hover zones
19
+ * - NO JS REQUIRED (CSS-driven)
20
+ */
21
+ const HoverGallery = component(
22
+ ({
23
+ images,
24
+ className = "",
25
+ as = "figure",
26
+ altPrefix = "Gallery image",
27
+ }: HoverGalleryProps) => {
28
+ const Tag = as;
29
+
30
+ const safeImages = images.slice(0, 10); // hard cap per DaisyUI
31
+
32
+ return createElement(
33
+ Tag,
34
+ {
35
+ class: `hover-gallery ${className}`.trim(),
36
+ role: "group",
37
+ "aria-label": "Hover image gallery",
38
+ },
39
+ safeImages.map((img, idx) => {
40
+ // string → <img src="..." />
41
+ if (typeof img === "string") {
42
+ return createElement("img", {
43
+ src: img,
44
+ alt: `${altPrefix} ${idx + 1}`,
45
+ loading: idx === 0 ? "eager" : "lazy",
46
+ draggable: false,
47
+ });
48
+ }
49
+
50
+ // JSX passed directly
51
+ return img;
52
+ })
53
+ );
54
+ }
55
+ );
56
+
57
+ export default HoverGallery;
@@ -0,0 +1,31 @@
1
+ // Kbd.ts
2
+ import { component, createElement } from "vaderjs";
3
+
4
+ export type KbdSize = "xs" | "sm" | "md" | "lg" | "xl";
5
+
6
+ export interface KbdProps {
7
+ children: any;
8
+ size?: KbdSize;
9
+ className?: string;
10
+ }
11
+
12
+ /**
13
+ * Kbd
14
+ * DaisyUI keyboard key / shortcut component
15
+ */
16
+ const Kbd = component(
17
+ ({ children, size = "md", className = "" }: KbdProps) => {
18
+ const sizeClass =
19
+ size === "md" ? "" : `kbd-${size}`;
20
+
21
+ return createElement(
22
+ "kbd",
23
+ {
24
+ class: `kbd ${sizeClass} ${className}`.trim(),
25
+ },
26
+ children
27
+ );
28
+ }
29
+ );
30
+
31
+ export default Kbd;