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,63 @@
1
+ import { component, createElement, VNode } from "vaderjs";
2
+
3
+ export type ButtonProps = {
4
+ color?: "neutral" | "primary" | "secondary" | "accent" | "info" | "success" | "warning" | "error";
5
+ style?: "outline" | "dash" | "soft" | "ghost" | "link";
6
+ size?: "xs" | "sm" | "md" | "lg" | "xl";
7
+ modifier?: "wide" | "block" | "square" | "circle";
8
+ active?: boolean;
9
+ disabled?: boolean;
10
+ className?: string;
11
+ onClick?: (e: MouseEvent) => void;
12
+ children?: VNode | VNode[] | string;
13
+ ariaLabel?: string;
14
+ };
15
+
16
+ const Button = component<ButtonProps>((props): VNode => {
17
+ const {
18
+ color,
19
+ style,
20
+ size = "md",
21
+ modifier,
22
+ active,
23
+ disabled,
24
+ className,
25
+ onClick,
26
+ children,
27
+ ariaLabel,
28
+ ...rest
29
+ } = props;
30
+
31
+ // Build class string
32
+ const classes = ["btn"];
33
+ if (color) classes.push(`btn-${color}`);
34
+ if (style) classes.push(`btn-${style}`);
35
+ if (size) classes.push(`btn-${size}`);
36
+ if (modifier) classes.push(`btn-${modifier}`);
37
+ if (active) classes.push("btn-active");
38
+ if (disabled) classes.push("btn-disabled");
39
+ if (className) classes.push(className);
40
+
41
+ // Convert camelCase props to proper DOM attributes
42
+ const domProps: Record<string, any> = {
43
+ class: classes.join(" "),
44
+ onClick: disabled ? undefined : onClick,
45
+ disabled,
46
+ ...rest,
47
+ };
48
+
49
+ // Add accessibility attributes with proper names
50
+ if (disabled) domProps["aria-disabled"] = "true";
51
+ if (active) domProps["aria-pressed"] = "true";
52
+ if (ariaLabel) domProps["aria-label"] = ariaLabel;
53
+
54
+
55
+ // Always return a VNode
56
+ return createElement(
57
+ "button",
58
+ domProps,
59
+ children
60
+ );
61
+ });
62
+
63
+ export default Button;
@@ -0,0 +1,104 @@
1
+ import { component, createElement, useState, useRef, useOnClickOutside, useEffect } from "vaderjs";
2
+
3
+ export type DropdownProps = {
4
+ method?: "details" | "popover" | "focus";
5
+ placement?: "start" | "center" | "end" | "top" | "bottom" | "left" | "right";
6
+ hover?: boolean;
7
+ open?: boolean;
8
+ close?: boolean;
9
+ buttonClass?: string;
10
+ contentClass?: string;
11
+ buttonContent: any;
12
+ children: any;
13
+ };
14
+
15
+ const Dropdown = component<DropdownProps>((props) => {
16
+ const {
17
+ method = "details",
18
+ placement = "bottom",
19
+ hover = false,
20
+ open,
21
+ close,
22
+ buttonClass = "btn",
23
+ contentClass = "menu dropdown-content bg-base-100 rounded-box shadow-md p-2",
24
+ buttonContent,
25
+ children,
26
+ } = props;
27
+
28
+ const [isOpen, setIsOpen] = useState(false);
29
+
30
+ // Create ref objects
31
+ const buttonRef = useRef<HTMLButtonElement | null>(null);
32
+ const contentRef = useRef<HTMLDivElement | null>(null);
33
+
34
+
35
+ const handleKeyDown = (e: KeyboardEvent) => {
36
+ if (!isOpen) return;
37
+ if (e.key === "Escape") {
38
+ setIsOpen(false);
39
+ buttonRef.current?.focus();
40
+ }
41
+ };
42
+
43
+ useEffect(() => {
44
+ console.log(contentRef)
45
+ if (!contentRef.current) return; // Only attach if element exists
46
+
47
+ const listener = (event: MouseEvent) => {
48
+ console.log(event)
49
+ if (!contentRef.current?.contains(event.target as Node)) {
50
+ setIsOpen(false);
51
+ }
52
+ };
53
+
54
+ document.addEventListener("mousedown", listener, true);
55
+ return () => document.removeEventListener("mousedown", listener, true);
56
+ }, [isOpen]); // re-run when isOpen changes
57
+
58
+ if (method === "details") {
59
+ return createElement(
60
+ "details",
61
+ {
62
+ class: `dropdown ${placement ? `dropdown-${placement}` : ""} ${hover ? "dropdown-hover" : ""} ${open ? "dropdown-open" : ""} ${close ? "dropdown-close" : ""}`,
63
+ },
64
+ createElement("summary", { class: buttonClass }, buttonContent),
65
+ createElement(
66
+ "div",
67
+ { class: contentClass, role: "menu", tabIndex: -1 },
68
+ children
69
+ )
70
+ );
71
+ }
72
+
73
+ // Popover or focus methods
74
+ return createElement(
75
+ "fragment",
76
+ null,
77
+ createElement(
78
+ "button",
79
+ {
80
+ // Pass ref as a property, not an attribute
81
+ ref: buttonRef,
82
+ class: buttonClass,
83
+ "aria-haspopup": "menu",
84
+ "aria-expanded": isOpen,
85
+ onClick: () => setIsOpen(!isOpen),
86
+ },
87
+ buttonContent
88
+ ),
89
+ isOpen &&
90
+ createElement(
91
+ "div",
92
+ {
93
+ // Pass ref as a property
94
+ ref: contentRef,
95
+ class: contentClass,
96
+ role: "menu",
97
+ tabIndex: -1
98
+ },
99
+ children
100
+ )
101
+ );
102
+ });
103
+
104
+ export default Dropdown;
@@ -0,0 +1,76 @@
1
+ // Fab.tsx
2
+ import { component, createElement, useState, useRef, useEffect } from "vaderjs";
3
+
4
+ interface FabProps {
5
+ mainIcon: any;
6
+ children?: any;
7
+ position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center";
8
+ direction?: "up" | "down" | "left" | "right";
9
+ }
10
+
11
+ interface FabItemProps {
12
+ icon?: any;
13
+ label?: string;
14
+ onClick?: () => void;
15
+ }
16
+
17
+ export const FabItem = component((props: FabItemProps) => {
18
+ const { icon, label, onClick } = props;
19
+
20
+ return createElement(
21
+ "div",
22
+ { className: "flex items-center gap-2" }, // wrapper div for DaisyUI
23
+ label && createElement("span", { className: "fab-item-label" }, label), // label outside button
24
+ createElement(
25
+ "button",
26
+ {
27
+ type: "button",
28
+ className: "btn btn-lg btn-circle fab-item",
29
+ onClick,
30
+ "aria-label": label,
31
+ },
32
+ icon // icon inside button
33
+ )
34
+ );
35
+ });
36
+
37
+
38
+
39
+ export const Fab = component((props: FabProps) => {
40
+ const { mainIcon, children, position = "bottom-right", direction = "up" } = props;
41
+ const [open, setOpen] = useState(false);
42
+ const containerRef = useRef<HTMLDivElement>(null);
43
+
44
+ const toggleOpen = () => setOpen(!open);
45
+
46
+ useEffect(() => {
47
+ const handleClickOutside = (e: MouseEvent) => {
48
+ if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
49
+ setOpen(false);
50
+ }
51
+ };
52
+ document.addEventListener("mousedown", handleClickOutside);
53
+ return () => document.removeEventListener("mousedown", handleClickOutside);
54
+ }, []);
55
+
56
+ return createElement(
57
+ "div",
58
+ { ref: containerRef, className: `fab ${position} ${direction}` },
59
+ // Main button
60
+ createElement(
61
+ "div",
62
+ {
63
+ tabIndex: 0,
64
+ role: "button",
65
+ className: "btn btn-lg btn-circle fab-main-btn",
66
+ onClick: toggleOpen,
67
+ },
68
+ mainIcon
69
+ ),
70
+ // Children (DaisyUI handles visibility/animation)
71
+ children
72
+ );
73
+ });
74
+
75
+
76
+ export default Fab;
@@ -0,0 +1,147 @@
1
+ import { useState, useEffect, component, createElement, VNode } from "vaderjs";
2
+ import Button from "../Button";
3
+
4
+ export function useModal(initialOpen = false) {
5
+ const [isOpen, setIsOpen] = useState(initialOpen);
6
+ const open = () => setIsOpen(true);
7
+ const close = () => setIsOpen(false);
8
+ const toggle = () => setIsOpen(!isOpen);
9
+ return { isOpen, open, close, toggle };
10
+ }
11
+
12
+ interface ModalProps {
13
+ isOpen: boolean;
14
+ onClose?: () => void;
15
+ title?: string;
16
+ children?: VNode | VNode[] | string;
17
+ size?: string; // DaisyUI sizes: w-11/12 max-w-md etc.
18
+ placement?: "top" | "middle" | "bottom";
19
+ horizontal?: "start" | "center" | "end"; // horizontal alignment
20
+ backdrop?: boolean; // show backdrop or not
21
+ openClass?: string; // extra classes when modal is open
22
+ }
23
+
24
+ interface ModalActionProps {
25
+ children: VNode | VNode[] | string;
26
+ onClick?: () => void;
27
+ closeModal?: boolean;
28
+ close?: () => void;
29
+ }
30
+
31
+ // Utility to get focusable elements inside the modal
32
+ function getFocusableElements(container: HTMLElement): HTMLElement[] {
33
+ return Array.from(
34
+ container.querySelectorAll(
35
+ 'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
36
+ )
37
+ ) as HTMLElement[];
38
+ }
39
+
40
+ export const Modal = component((props: ModalProps) => {
41
+ const {
42
+ isOpen,
43
+ onClose,
44
+ title,
45
+ children,
46
+ size = "w-11/12 max-w-md",
47
+ placement = "middle",
48
+ horizontal = "center",
49
+ backdrop = true,
50
+ openClass = "",
51
+ } = props;
52
+
53
+ let modalRef: HTMLDivElement | null = null;
54
+
55
+ const handleClose = () => {
56
+ if (onClose) onClose();
57
+ };
58
+
59
+ // ESC key & focus trap
60
+ useEffect(() => {
61
+ if (!isOpen) return;
62
+
63
+ const handleKey = (e: KeyboardEvent) => {
64
+ if (e.key === "Escape") handleClose();
65
+ if (e.key === "Tab" && modalRef) {
66
+ const focusables = getFocusableElements(modalRef);
67
+ if (focusables.length === 0) return;
68
+ const first = focusables[0];
69
+ const last = focusables[focusables.length - 1];
70
+
71
+ if (e.shiftKey) {
72
+ if (document.activeElement === first) {
73
+ e.preventDefault();
74
+ last.focus();
75
+ }
76
+ } else {
77
+ if (document.activeElement === last) {
78
+ e.preventDefault();
79
+ first.focus();
80
+ }
81
+ }
82
+ }
83
+ };
84
+
85
+ document.addEventListener("keydown", handleKey);
86
+ return () => document.removeEventListener("keydown", handleKey);
87
+ }, [isOpen]);
88
+
89
+ // Autofocus first element
90
+ useEffect(() => {
91
+ if (isOpen && modalRef) {
92
+ const focusables = getFocusableElements(modalRef);
93
+ if (focusables.length > 0) focusables[0].focus();
94
+ }
95
+ }, [isOpen]);
96
+
97
+ if (!isOpen) return null;
98
+
99
+ // DaisyUI placement classes
100
+ const placementClass =
101
+ placement === "top"
102
+ ? "modal-top"
103
+ : placement === "bottom"
104
+ ? "modal-bottom"
105
+ : "modal-middle"; // default
106
+
107
+ const horizontalClass =
108
+ horizontal === "start"
109
+ ? "modal-start"
110
+ : horizontal === "end"
111
+ ? "modal-end"
112
+ : "modal-center"; // center default
113
+
114
+ return createElement(
115
+ "div",
116
+ {
117
+ className: `modal modal-open ${placementClass} ${openClass}`,
118
+ "aria-modal": "true",
119
+ role: "dialog",
120
+ },
121
+ backdrop &&
122
+ createElement("div", {
123
+ className: "modal-backdrop",
124
+ onClick: handleClose,
125
+ }),
126
+ createElement(
127
+ "div",
128
+ {
129
+ className: `modal-box ${size} relative`,
130
+ ref: (el: HTMLDivElement) => (modalRef = el),
131
+ },
132
+ title && createElement("h3", { className: "font-bold text-lg" }, title),
133
+ children
134
+ )
135
+ );
136
+ });
137
+
138
+ export const ModalAction = component((props: ModalActionProps) => {
139
+ const { children, onClick, closeModal, close } = props;
140
+
141
+ const handleClick = () => {
142
+ if (onClick) onClick();
143
+ if (closeModal && close) close();
144
+ };
145
+
146
+ return createElement(Button, { onClick: handleClick }, children);
147
+ });
@@ -0,0 +1,52 @@
1
+ import { useState, component, createElement, VNode } from "vaderjs";
2
+
3
+ export type SwapProps = {
4
+ on?: VNode | VNode[] | string;
5
+ off?: VNode | VNode[] | string;
6
+ indeterminate?: VNode | VNode[] | string;
7
+ active?: boolean; // Controlled toggle
8
+ rotate?: boolean;
9
+ flip?: boolean;
10
+ className?: string;
11
+ onChange?: (active: boolean) => void;
12
+ clickable?: boolean; // If true, label click toggles state
13
+ };
14
+
15
+ export const Swap = component((props: SwapProps) => {
16
+ const { on, off, indeterminate, active, rotate, flip, className, onChange, clickable } = props;
17
+
18
+ const [internalActive, setInternalActive] = useState(active ?? false);
19
+ const isControlled = active !== undefined;
20
+ const currentActive = isControlled ? active : internalActive;
21
+
22
+ const handleClick = () => {
23
+ if (!clickable) return;
24
+ const newState = !currentActive;
25
+ if (!isControlled) setInternalActive(newState);
26
+ if (onChange) onChange(newState);
27
+ };
28
+
29
+ const swapClasses = ["swap"];
30
+ if (rotate) swapClasses.push("swap-rotate");
31
+ if (flip) swapClasses.push("swap-flip");
32
+ if (currentActive) swapClasses.push("swap-active");
33
+ if (className) swapClasses.push(className);
34
+
35
+ return createElement(
36
+ "label",
37
+ { className: swapClasses.join(" "), onClick: handleClick },
38
+ // Only render checkbox if not clickable or for accessibility
39
+ !clickable && createElement("input", {
40
+ type: "checkbox",
41
+ checked: currentActive,
42
+ onChange: (e: Event) => {
43
+ const checked = (e.target as HTMLInputElement).checked;
44
+ if (!isControlled) setInternalActive(checked);
45
+ if (onChange) onChange(checked);
46
+ }
47
+ }),
48
+ on && createElement("div", { className: "swap-on" }, on),
49
+ off && createElement("div", { className: "swap-off" }, off),
50
+ indeterminate && createElement("div", { className: "swap-indeterminate" }, indeterminate)
51
+ );
52
+ });
@@ -0,0 +1,133 @@
1
+ import { component, createElement, useState, VNode } from "vaderjs";
2
+
3
+ export type ThemeOption = {
4
+ value: string;
5
+ label?: string;
6
+ icon?: VNode; // optional icon
7
+ ariaLabel?: string;
8
+ };
9
+
10
+ export type ThemeControllerProps = {
11
+ type?: "checkbox" | "radio" | "toggle" | "swap" | "buttons" | "dropdown"; // type of input
12
+ value?: string | boolean; // initial value
13
+ options?: ThemeOption[]; // for radios, buttons, dropdown
14
+ className?: string;
15
+ direction?: "vertical" | "horizontal"; // for buttons/radios
16
+ onChange?: (value: string | boolean) => void;
17
+ swapRotate?: boolean;
18
+ swapFlip?: boolean;
19
+ };
20
+
21
+ export const ThemeController = component((props: ThemeControllerProps) => {
22
+ const {
23
+ type = "checkbox",
24
+ value,
25
+ options = [],
26
+ className,
27
+ direction = "horizontal",
28
+ onChange,
29
+ swapRotate,
30
+ swapFlip,
31
+ } = props;
32
+
33
+ const [state, setState] = useState(value ?? (type === "checkbox" || type === "toggle" || type === "swap" ? false : ""));
34
+
35
+ const handleChange = (val: string | boolean) => {
36
+ setState(val);
37
+ if (onChange) onChange(val);
38
+ };
39
+
40
+ // Checkbox / Toggle / Swap
41
+ if (type === "checkbox" || type === "toggle") {
42
+ return createElement("input", {
43
+ type: "checkbox",
44
+ className: `theme-controller ${type} ${className || ""}`,
45
+ checked: state as boolean,
46
+ onChange: (e: Event) => handleChange((e.target as HTMLInputElement).checked),
47
+ });
48
+ }
49
+
50
+ if (type === "swap") {
51
+ // swap-on / swap-off
52
+ const [checked, setChecked] = useState(state as boolean);
53
+ return createElement(
54
+ "label",
55
+ {
56
+ className: `swap ${swapRotate ? "swap-rotate" : ""} ${swapFlip ? "swap-flip" : ""} ${checked ? "swap-active" : ""} ${className || ""}`,
57
+ onClick: () => handleChange(!checked),
58
+ },
59
+ createElement("input", {
60
+ type: "checkbox",
61
+ className: "theme-controller hidden",
62
+ checked,
63
+ onChange: (e: Event) => handleChange((e.target as HTMLInputElement).checked),
64
+ }),
65
+ options[0]?.icon && createElement("div", { className: "swap-off" }, options[0].icon),
66
+ options[1]?.icon && createElement("div", { className: "swap-on" }, options[1].icon)
67
+ );
68
+ }
69
+
70
+ // Radio / Buttons / Dropdown
71
+ if (type === "radio" || type === "buttons") {
72
+ const containerClass = type === "buttons" ? `join ${direction === "vertical" ? "join-vertical" : ""}` : "";
73
+ return createElement(
74
+ "div",
75
+ { className: `${containerClass} ${className || ""}` },
76
+ options.map(opt =>
77
+ createElement("input", {
78
+ type: "radio",
79
+ name: type === "radio" ? "theme-radios" : "theme-buttons",
80
+ className: `theme-controller ${type === "buttons" ? "btn join-item" : "radio"} ${opt?.ariaLabel ? "" : ""}`,
81
+ value: opt.value,
82
+ checked: state === opt.value,
83
+ "aria-label": opt.ariaLabel ?? opt.label ?? opt.value,
84
+ onChange: () => handleChange(opt.value),
85
+ })
86
+ )
87
+ );
88
+ }
89
+
90
+ if (type === "dropdown") {
91
+ return createElement(
92
+ "div",
93
+ { className: `dropdown ${className || ""}` },
94
+ createElement(
95
+ "div",
96
+ { tabIndex: 0, role: "button", className: "btn m-1" },
97
+ "Theme",
98
+ createElement(
99
+ "svg",
100
+ {
101
+ width: "12px",
102
+ height: "12px",
103
+ className: "inline-block h-2 w-2 fill-current opacity-60",
104
+ xmlns: "http://www.w3.org/2000/svg",
105
+ viewBox: "0 0 2048 2048",
106
+ },
107
+ createElement("path", { d: "M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z" })
108
+ )
109
+ ),
110
+ createElement(
111
+ "ul",
112
+ { tabIndex: -1, className: "dropdown-content bg-base-300 rounded-box z-1 w-52 p-2 shadow-2xl" },
113
+ options.map(opt =>
114
+ createElement(
115
+ "li",
116
+ {},
117
+ createElement("input", {
118
+ type: "radio",
119
+ name: "theme-dropdown",
120
+ className: "theme-controller w-full btn btn-sm btn-block btn-ghost justify-start",
121
+ value: opt.value,
122
+ checked: state === opt.value,
123
+ "aria-label": opt.ariaLabel ?? opt.label ?? opt.value,
124
+ onChange: () => handleChange(opt.value),
125
+ })
126
+ )
127
+ )
128
+ )
129
+ );
130
+ }
131
+
132
+ return null;
133
+ });
@@ -0,0 +1,82 @@
1
+ import { component, createElement, VNode } from "vaderjs";
2
+
3
+ export type AccordionItem = {
4
+ title: string | VNode;
5
+ content: string | VNode;
6
+ value?: string; // for radio inputs
7
+ open?: boolean; // for details
8
+ };
9
+
10
+ export type AccordionProps = {
11
+ items: AccordionItem[];
12
+ type?: "radio" | "details"; // radio inputs or details elements
13
+ name?: string; // radio group name
14
+ arrow?: boolean; // add arrow icon
15
+ plus?: boolean; // add plus/minus icon
16
+ join?: boolean; // join items for seamless borders
17
+ direction?: "vertical" | "horizontal"; // join direction
18
+ className?: string; // additional container class
19
+ border?: boolean; // add border to items
20
+ onChange?: (value: string) => void;
21
+ };
22
+
23
+ export const Accordion = component((props: AccordionProps) => {
24
+ const {
25
+ items,
26
+ type = "radio",
27
+ name = "accordion",
28
+ arrow,
29
+ plus,
30
+ join,
31
+ direction = "vertical",
32
+ className,
33
+ border = true,
34
+ onChange,
35
+ } = props;
36
+
37
+ const containerClass = join ? `join ${direction === "vertical" ? "join-vertical" : ""}` : "";
38
+
39
+ return createElement(
40
+ "div",
41
+ { className: `${containerClass} ${className || ""}` },
42
+ items.map((item, index) => {
43
+ const itemClasses = ["collapse"];
44
+ if (arrow) itemClasses.push("collapse-arrow");
45
+ if (plus) itemClasses.push("collapse-plus");
46
+ if (border) itemClasses.push("border border-base-300");
47
+ if (join) itemClasses.push("join-item");
48
+
49
+ if (type === "details" && item.open) itemClasses.push("collapse-open");
50
+
51
+ const handleRadioChange = () => {
52
+ if (onChange && item.value) onChange(item.value);
53
+ };
54
+
55
+ if (type === "details") {
56
+ return createElement(
57
+ "details",
58
+ {
59
+ className: itemClasses.join(" "),
60
+ open: !!item.open,
61
+ },
62
+ createElement("summary", { className: "collapse-title font-semibold" }, item.title),
63
+ createElement("div", { className: "collapse-content text-sm" }, item.content)
64
+ );
65
+ }
66
+
67
+ // radio type
68
+ return createElement(
69
+ "div",
70
+ { className: itemClasses.join(" ") },
71
+ createElement("input", {
72
+ type: "radio",
73
+ name,
74
+ checked: index === 0,
75
+ onChange: handleRadioChange,
76
+ }),
77
+ createElement("div", { className: "collapse-title font-semibold" }, item.title),
78
+ createElement("div", { className: "collapse-content text-sm" }, item.content)
79
+ );
80
+ })
81
+ );
82
+ });