@neuctra/ui 0.2.4 → 0.2.6
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/dist/components/basic/Accordation.d.ts +9 -13
- package/dist/components/basic/Alert.d.ts +10 -23
- package/dist/components/basic/Avatar.d.ts +7 -16
- package/dist/components/basic/Badge.d.ts +9 -14
- package/dist/components/basic/Button.d.ts +9 -19
- package/dist/components/basic/CheckboxGroup.d.ts +1 -0
- package/dist/components/basic/Container.d.ts +2 -19
- package/dist/components/basic/Drawer.d.ts +7 -18
- package/dist/components/basic/DropDown.d.ts +20 -40
- package/dist/components/basic/FlexView.d.ts +16 -0
- package/dist/components/basic/GridView.d.ts +4 -9
- package/dist/components/basic/Image.d.ts +10 -31
- package/dist/components/basic/Input.d.ts +22 -35
- package/dist/components/basic/List.d.ts +6 -20
- package/dist/components/basic/Modal.d.ts +8 -8
- package/dist/components/basic/RadioGroup.d.ts +1 -0
- package/dist/components/basic/Stack.d.ts +4 -14
- package/dist/components/basic/SwitchGroup.d.ts +1 -0
- package/dist/components/basic/Table.d.ts +6 -1
- package/dist/index.cjs.js +82 -52
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.es.js +2059 -2861
- package/dist/index.es.js.map +1 -1
- package/dist/src/components/basic/Accordation.js +25 -26
- package/dist/src/components/basic/Alert.js +33 -138
- package/dist/src/components/basic/AudioPlayer.js +54 -40
- package/dist/src/components/basic/Avatar.js +41 -154
- package/dist/src/components/basic/Badge.js +23 -62
- package/dist/src/components/basic/Button.js +24 -97
- package/dist/src/components/basic/CheckboxGroup.js +36 -13
- package/dist/src/components/basic/Container.js +19 -38
- package/dist/src/components/basic/Drawer.js +22 -73
- package/dist/src/components/basic/DropDown.js +94 -158
- package/dist/src/components/basic/FlexView.js +19 -0
- package/dist/src/components/basic/GridView.js +15 -48
- package/dist/src/components/basic/Image.js +39 -79
- package/dist/src/components/basic/Input.js +68 -109
- package/dist/src/components/basic/List.js +20 -62
- package/dist/src/components/basic/Modal.js +6 -58
- package/dist/src/components/basic/RadioGroup.js +35 -18
- package/dist/src/components/basic/Stack.js +19 -72
- package/dist/src/components/basic/SwitchGroup.js +42 -16
- package/dist/src/components/basic/Table.js +25 -27
- package/dist/src/components/basic/Tabs.js +3 -12
- package/dist/src/index.js +3 -5
- package/dist/types/src/components/basic/Accordation.d.ts +9 -13
- package/dist/types/src/components/basic/Alert.d.ts +10 -23
- package/dist/types/src/components/basic/Avatar.d.ts +7 -16
- package/dist/types/src/components/basic/Badge.d.ts +9 -14
- package/dist/types/src/components/basic/Button.d.ts +9 -19
- package/dist/types/src/components/basic/CheckboxGroup.d.ts +1 -0
- package/dist/types/src/components/basic/Container.d.ts +2 -19
- package/dist/types/src/components/basic/Drawer.d.ts +7 -18
- package/dist/types/src/components/basic/DropDown.d.ts +20 -40
- package/dist/types/src/components/basic/FlexView.d.ts +16 -0
- package/dist/types/src/components/basic/GridView.d.ts +4 -9
- package/dist/types/src/components/basic/Image.d.ts +10 -31
- package/dist/types/src/components/basic/Input.d.ts +22 -35
- package/dist/types/src/components/basic/List.d.ts +6 -20
- package/dist/types/src/components/basic/Modal.d.ts +8 -8
- package/dist/types/src/components/basic/RadioGroup.d.ts +1 -0
- package/dist/types/src/components/basic/Stack.d.ts +4 -14
- package/dist/types/src/components/basic/SwitchGroup.d.ts +1 -0
- package/dist/types/src/components/basic/Table.d.ts +6 -1
- package/dist/types/src/index.d.ts +3 -4
- package/dist/ui.css +1 -1
- package/package.json +2 -1
- package/dist/components/avatar/AvatarGroup.d.ts +0 -9
- package/dist/components/avatar/AvatarWithStatus.d.ts +0 -10
- package/dist/components/basic/Card.d.ts +0 -28
- package/dist/components/basic/Flexbox.d.ts +0 -25
- package/dist/components/basic/Section.d.ts +0 -36
- package/dist/src/components/avatar/AvatarGroup.js +0 -9
- package/dist/src/components/avatar/AvatarWithStatus.js +0 -18
- package/dist/src/components/basic/Card.js +0 -47
- package/dist/src/components/basic/Flexbox.js +0 -67
- package/dist/src/components/basic/Section.js +0 -100
- package/dist/types/src/components/avatar/AvatarGroup.d.ts +0 -9
- package/dist/types/src/components/avatar/AvatarWithStatus.d.ts +0 -10
- package/dist/types/src/components/basic/Card.d.ts +0 -28
- package/dist/types/src/components/basic/Flexbox.d.ts +0 -25
- package/dist/types/src/components/basic/Section.d.ts +0 -36
|
@@ -1,66 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { memo } from "react";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
fontSize,
|
|
14
|
-
fontWeight,
|
|
15
|
-
margin,
|
|
16
|
-
boxShadow: shadow,
|
|
17
|
-
position: "relative",
|
|
18
|
-
cursor: onClick ? "pointer" : "default",
|
|
19
|
-
userSelect: "none",
|
|
20
|
-
lineHeight: 1,
|
|
21
|
-
transition: "all 0.2s ease",
|
|
22
|
-
...style,
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
/* -------------------------------------------------------------------------- */
|
|
6
|
+
/* 🏷 Badge */
|
|
7
|
+
/* -------------------------------------------------------------------------- */
|
|
8
|
+
export const Badge = memo(({ text, icon, iconPosition = "left", primaryTheme = true, primaryColor = "#3b82f6", size = "md", rounded = true, notificationDot = false, dotColor = "#ef4444", count, pulse = false, className, onClick, }) => {
|
|
9
|
+
/** 📏 Sizes */
|
|
10
|
+
const sizes = {
|
|
11
|
+
sm: "px-2 py-0.5 text-xs",
|
|
12
|
+
md: "px-3 py-1 text-xs",
|
|
13
|
+
lg: "px-4 py-1.5 text-sm",
|
|
23
14
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
position: "absolute",
|
|
36
|
-
top: "-8px",
|
|
37
|
-
right: "-8px",
|
|
38
|
-
backgroundColor: dotColor,
|
|
39
|
-
color: "#fff",
|
|
40
|
-
borderRadius: "50%",
|
|
41
|
-
minWidth: "18px",
|
|
42
|
-
height: "18px",
|
|
43
|
-
fontSize: "11px",
|
|
44
|
-
padding: "0 5px",
|
|
45
|
-
display: "flex",
|
|
46
|
-
alignItems: "center",
|
|
47
|
-
justifyContent: "center",
|
|
48
|
-
lineHeight: 1,
|
|
49
|
-
};
|
|
50
|
-
const iconStyle = {
|
|
51
|
-
display: "flex",
|
|
52
|
-
alignItems: "center",
|
|
53
|
-
margin: icon && text
|
|
54
|
-
? iconPosition === "left"
|
|
55
|
-
? "0 6px 0 0"
|
|
56
|
-
: "0 0 0 6px"
|
|
57
|
-
: 0,
|
|
58
|
-
};
|
|
59
|
-
return (_jsxs("span", { style: baseStyle, className: className, onClick: onClick, children: [notificationDot && _jsx("span", { style: dotStyle }), typeof count !== "undefined" && _jsx("span", { style: countStyle, children: count }), icon && iconPosition === "left" && _jsx("span", { style: iconStyle, children: icon }), text && _jsx("span", { children: text }), icon && iconPosition === "right" && _jsx("span", { style: iconStyle, children: icon }), _jsx("style", { children: `
|
|
60
|
-
@keyframes pulseAnim {
|
|
61
|
-
0% { transform: scale(1); opacity: 1; }
|
|
62
|
-
50% { transform: scale(1.5); opacity: 0.5; }
|
|
63
|
-
100% { transform: scale(1); opacity: 1; }
|
|
64
|
-
}
|
|
65
|
-
` })] }));
|
|
15
|
+
/** 🎨 Theme */
|
|
16
|
+
const themeClasses = primaryTheme
|
|
17
|
+
? "bg-[var(--primary)] text-white"
|
|
18
|
+
: "";
|
|
19
|
+
const dynamicStyle = !primaryTheme
|
|
20
|
+
? {
|
|
21
|
+
backgroundColor: primaryColor,
|
|
22
|
+
color: "#fff",
|
|
23
|
+
}
|
|
24
|
+
: {};
|
|
25
|
+
return (_jsxs("span", { onClick: onClick, style: !primaryTheme ? dynamicStyle : undefined, className: clsx("relative inline-flex items-center justify-center gap-1 font-medium", "transition-all duration-200 select-none", rounded ? "rounded-full" : "rounded-md", sizes[size], themeClasses, onClick && "cursor-pointer hover:opacity-90", className), children: [notificationDot && (_jsx("span", { style: !primaryTheme ? { backgroundColor: dotColor } : undefined, className: clsx("absolute -top-1 -right-1 w-2 h-2 rounded-full", pulse && "animate-ping", primaryTheme ? "bg-red-500" : "") })), count !== undefined && (_jsx("span", { style: !primaryTheme ? { backgroundColor: dotColor } : undefined, className: clsx("absolute -top-2 -right-2 min-w-[18px] h-[18px] px-1 text-[10px]", "flex items-center justify-center rounded-full text-white", primaryTheme ? "bg-red-500" : ""), children: count })), icon && iconPosition === "left" && (_jsx("span", { className: "flex items-center", children: icon })), text && _jsx("span", { children: text }), icon && iconPosition === "right" && (_jsx("span", { className: "flex items-center", children: icon }))] }));
|
|
66
26
|
});
|
|
27
|
+
Badge.displayName = "Badge";
|
|
@@ -1,101 +1,28 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
success: "#16a34a",
|
|
10
|
-
danger: "#dc2626",
|
|
11
|
-
white: "#ffffff",
|
|
12
|
-
muted: "#6b7280",
|
|
13
|
-
border: "#d1d5db",
|
|
14
|
-
hover: "#1d4ed8",
|
|
15
|
-
text: "#ffffff",
|
|
16
|
-
},
|
|
17
|
-
dark: {
|
|
18
|
-
default: "#f8fafc",
|
|
19
|
-
primary: "#60a5fa",
|
|
20
|
-
success: "#22c55e",
|
|
21
|
-
danger: "#f87171",
|
|
22
|
-
white: "#ffffff",
|
|
23
|
-
muted: "#9ca3af",
|
|
24
|
-
border: "#374151",
|
|
25
|
-
hover: "#3b82f6",
|
|
26
|
-
text: "#000000",
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
/** 🎨 Adjust color shade utility */
|
|
30
|
-
function adjustColor(color, amount) {
|
|
31
|
-
if (!color.startsWith("#") || color.length !== 7)
|
|
32
|
-
return color;
|
|
33
|
-
return ("#" +
|
|
34
|
-
color
|
|
35
|
-
.replace(/^#/, "")
|
|
36
|
-
.replace(/../g, (hex) => ("0" +
|
|
37
|
-
Math.min(255, Math.max(0, parseInt(hex, 16) + amount)).toString(16)).slice(-2)));
|
|
38
|
-
}
|
|
39
|
-
/** 💎 Minimal customizable Button */
|
|
40
|
-
export const Button = ({ children, type = "button", onClick, iconBefore, iconAfter, className = "", style, fullWidth = false, disabled = false, loading = false, loadingText = "Loading...", darkMode = false, baseColor, size = "md", rounded = true, showBorder = false, // ✅ default: no border
|
|
41
|
-
}) => {
|
|
42
|
-
const [hovered, setHovered] = useState(false);
|
|
43
|
-
/** 🧠 Theme system */
|
|
44
|
-
const theme = useMemo(() => {
|
|
45
|
-
if (!baseColor)
|
|
46
|
-
return defaultColors[darkMode ? "dark" : "light"];
|
|
47
|
-
const light = {
|
|
48
|
-
primary: baseColor,
|
|
49
|
-
hover: adjustColor(baseColor, -40),
|
|
50
|
-
text: "#ffffff",
|
|
51
|
-
border: adjustColor(baseColor, -60),
|
|
52
|
-
};
|
|
53
|
-
const dark = {
|
|
54
|
-
primary: adjustColor(baseColor, 60),
|
|
55
|
-
hover: adjustColor(baseColor, 80),
|
|
56
|
-
text: "#000000",
|
|
57
|
-
border: adjustColor(baseColor, 40),
|
|
58
|
-
};
|
|
59
|
-
return darkMode ? dark : light;
|
|
60
|
-
}, [baseColor, darkMode]);
|
|
3
|
+
import { forwardRef } from "react";
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
/* -------------------------------------------------------------------------- */
|
|
6
|
+
/* 🔘 Button */
|
|
7
|
+
/* -------------------------------------------------------------------------- */
|
|
8
|
+
export const Button = forwardRef(({ children, iconBefore, iconAfter, loading = false, loadingText = "Loading...", fullWidth = false, primaryTheme = true, primaryColor = "#3b82f6", size = "md", disabled, className, ...props }, ref) => {
|
|
61
9
|
/** 📏 Sizes */
|
|
62
|
-
const
|
|
63
|
-
sm:
|
|
64
|
-
md:
|
|
65
|
-
lg:
|
|
10
|
+
const sizeClasses = {
|
|
11
|
+
sm: "px-3 py-1.5 text-sm",
|
|
12
|
+
md: "px-5 py-2.5 text-sm",
|
|
13
|
+
lg: "px-6 py-3 text-base",
|
|
66
14
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
color: theme.text,
|
|
82
|
-
cursor: disabled ? "not-allowed" : "pointer",
|
|
83
|
-
opacity: disabled ? 0.6 : 1,
|
|
84
|
-
transition: "all 0.25s ease-in-out",
|
|
85
|
-
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.15)",
|
|
86
|
-
...style,
|
|
87
|
-
};
|
|
88
|
-
return (_jsxs("button", { type: type, className: className, style: buttonStyle, onClick: !disabled && !loading ? onClick : undefined, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), disabled: disabled, children: [loading ? (_jsxs(_Fragment, { children: [_jsx("span", { style: {
|
|
89
|
-
width: "16px",
|
|
90
|
-
height: "16px",
|
|
91
|
-
border: "2px solid currentColor",
|
|
92
|
-
borderTopColor: "transparent",
|
|
93
|
-
borderRadius: "50%",
|
|
94
|
-
animation: "spin 1s linear infinite",
|
|
95
|
-
} }), _jsx("span", { children: loadingText })] })) : (_jsxs(_Fragment, { children: [iconBefore && _jsx("span", { children: iconBefore }), _jsx("span", { children: children }), iconAfter && _jsx("span", { children: iconAfter })] })), _jsx("style", { children: `
|
|
96
|
-
@keyframes spin {
|
|
97
|
-
to { transform: rotate(360deg); }
|
|
98
|
-
}
|
|
99
|
-
` })] }));
|
|
100
|
-
};
|
|
101
|
-
export default Button;
|
|
15
|
+
/** 🎨 Theme styles */
|
|
16
|
+
const themeClasses = primaryTheme
|
|
17
|
+
? "bg-[var(--primary)] text-white hover:bg-[var(--primary)]/90 focus:ring-[var(--primary)]/30"
|
|
18
|
+
: "";
|
|
19
|
+
/** 🎨 Dynamic fallback for non-primary theme */
|
|
20
|
+
const dynamicStyle = !primaryTheme
|
|
21
|
+
? {
|
|
22
|
+
backgroundColor: primaryColor,
|
|
23
|
+
color: "#fff",
|
|
24
|
+
}
|
|
25
|
+
: {};
|
|
26
|
+
return (_jsx("button", { ref: ref, disabled: disabled || loading, style: !primaryTheme ? dynamicStyle : undefined, className: clsx("inline-flex items-center justify-center gap-2 font-medium rounded-lg transition-all duration-200", "focus:outline-none focus:ring-2 focus:ring-offset-1", sizeClasses[size], themeClasses, fullWidth && "w-full", (disabled || loading) && "opacity-60 cursor-not-allowed", className), ...props, children: loading ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "w-4 h-4 border-2 border-current border-t-transparent rounded-full animate-spin" }), _jsx("span", { children: loadingText })] })) : (_jsxs(_Fragment, { children: [iconBefore && (_jsx("span", { className: "flex items-center justify-center", children: iconBefore })), _jsx("span", { children: children }), iconAfter && (_jsx("span", { className: "flex items-center justify-center", children: iconAfter }))] })) }));
|
|
27
|
+
});
|
|
28
|
+
Button.displayName = "Button";
|
|
@@ -1,26 +1,49 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
|
|
3
|
+
import { useState, useEffect, useRef } from "react";
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
export const CheckboxGroup = ({ name, options, selectedValues = [], onChange, disabled = false, readOnly = false, required = false, error, className, customIcon, style, labelStyle, iconSize = 20, iconCheckedBgColor = "#2563eb", iconUncheckedBorderColor = "#9ca3af", textColor = "#374151", errorStyle, darkMode = false, }) => {
|
|
6
|
+
const containerRef = useRef(null);
|
|
7
|
+
const [focusedIndex, setFocusedIndex] = useState(null);
|
|
4
8
|
const handleChange = (value) => {
|
|
5
|
-
if (!onChange)
|
|
9
|
+
if (!onChange || disabled || readOnly)
|
|
6
10
|
return;
|
|
7
11
|
const updatedValues = selectedValues.includes(value)
|
|
8
12
|
? selectedValues.filter((v) => v !== value)
|
|
9
13
|
: [...selectedValues, value];
|
|
10
14
|
onChange(updatedValues);
|
|
11
15
|
};
|
|
12
|
-
|
|
16
|
+
// Keyboard navigation (space/enter toggle)
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const container = containerRef.current;
|
|
19
|
+
if (!container)
|
|
20
|
+
return;
|
|
21
|
+
const handleKeyDown = (e) => {
|
|
22
|
+
if (disabled)
|
|
23
|
+
return;
|
|
24
|
+
if (focusedIndex === null)
|
|
25
|
+
return;
|
|
26
|
+
const currentIndex = focusedIndex;
|
|
27
|
+
if (e.key === "ArrowDown" || e.key === "ArrowRight") {
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
setFocusedIndex((currentIndex + 1) % options.length);
|
|
30
|
+
}
|
|
31
|
+
if (e.key === "ArrowUp" || e.key === "ArrowLeft") {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
setFocusedIndex((currentIndex - 1 + options.length) % options.length);
|
|
34
|
+
}
|
|
35
|
+
if (e.key === " " || e.key === "Enter") {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
handleChange(options[currentIndex].value);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
container.addEventListener("keydown", handleKeyDown);
|
|
41
|
+
return () => container.removeEventListener("keydown", handleKeyDown);
|
|
42
|
+
}, [focusedIndex, options, selectedValues, disabled]);
|
|
43
|
+
return (_jsxs("div", { ref: containerRef, role: "group", "aria-disabled": disabled, "aria-invalid": !!error, tabIndex: 0, className: clsx("flex flex-col gap-2", className), style: { ...style }, children: [options.map((option, index) => {
|
|
13
44
|
const isChecked = selectedValues.includes(option.value);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
alignItems: "center",
|
|
17
|
-
justifyContent: "space-between",
|
|
18
|
-
cursor: disabled ? "not-allowed" : "pointer",
|
|
19
|
-
opacity: disabled ? 0.6 : 1,
|
|
20
|
-
gap: 8,
|
|
21
|
-
userSelect: "none",
|
|
22
|
-
...labelStyle,
|
|
23
|
-
}, children: [_jsx("span", { style: { color: textColor, fontSize: 14 }, children: option.label }), _jsx("input", { type: "checkbox", name: name, value: option.value, checked: isChecked, disabled: disabled || readOnly, required: required, onChange: () => handleChange(option.value), style: { display: "none" } }), customIcon ? (customIcon(isChecked)) : (_jsx("span", { style: {
|
|
45
|
+
const isFocused = focusedIndex === index;
|
|
46
|
+
return (_jsxs("label", { className: clsx("flex items-center justify-between cursor-pointer select-none transition-opacity", disabled ? "opacity-50 cursor-not-allowed" : "opacity-100", isFocused ? "ring-2 ring-blue-400" : ""), style: { ...labelStyle }, onFocus: () => setFocusedIndex(index), children: [_jsx("span", { style: { color: textColor, fontSize: 14 }, children: option.label }), _jsx("input", { type: "checkbox", name: name, value: option.value, checked: isChecked, disabled: disabled || readOnly, required: required, onChange: () => handleChange(option.value), style: { display: "none" } }), customIcon ? (customIcon(isChecked)) : (_jsx("span", { style: {
|
|
24
47
|
display: "inline-flex",
|
|
25
48
|
justifyContent: "center",
|
|
26
49
|
alignItems: "center",
|
|
@@ -1,45 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
const sizeMap = {
|
|
4
|
+
sm: "max-w-sm",
|
|
5
|
+
md: "max-w-md",
|
|
6
|
+
lg: "max-w-lg",
|
|
7
|
+
xl: "max-w-xl",
|
|
8
|
+
"2xl": "max-w-2xl",
|
|
9
|
+
full: "w-full",
|
|
10
|
+
};
|
|
11
|
+
const paddingMap = {
|
|
12
|
+
none: "p-0",
|
|
13
|
+
sm: "p-4",
|
|
14
|
+
md: "p-6",
|
|
15
|
+
lg: "p-8",
|
|
16
|
+
xl: "p-12",
|
|
11
17
|
};
|
|
12
18
|
/**
|
|
13
19
|
* 🧱 Container Component
|
|
14
|
-
*
|
|
20
|
+
* Fully responsive, Tailwind-first layout wrapper
|
|
15
21
|
*/
|
|
16
|
-
export const Container = ({ size = "lg", padding =
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const computedMargin = center && margin === "0 auto" ? "0 auto" : margin;
|
|
20
|
-
const computedBorderRadius = typeof borderRadius === "number" ? `${borderRadius}px` : borderRadius;
|
|
21
|
-
return {
|
|
22
|
-
maxWidth: width ?? containerWidths[size],
|
|
23
|
-
margin: computedMargin,
|
|
24
|
-
padding: computedPadding,
|
|
25
|
-
backgroundColor,
|
|
26
|
-
height,
|
|
27
|
-
borderRadius: computedBorderRadius,
|
|
28
|
-
boxSizing: "border-box",
|
|
29
|
-
width: width ?? "100%",
|
|
30
|
-
...style,
|
|
31
|
-
};
|
|
32
|
-
}, [
|
|
33
|
-
size,
|
|
34
|
-
padding,
|
|
35
|
-
margin,
|
|
36
|
-
backgroundColor,
|
|
37
|
-
width,
|
|
38
|
-
height,
|
|
39
|
-
borderRadius,
|
|
40
|
-
center,
|
|
41
|
-
style,
|
|
42
|
-
]);
|
|
43
|
-
return (_jsx("div", { className: className, style: styles, children: children }));
|
|
22
|
+
export const Container = ({ size = "lg", padding = "md", center = true, className = "", children, }) => {
|
|
23
|
+
const classes = clsx("w-full box-border", sizeMap[size], paddingMap[padding], center && "mx-auto", className);
|
|
24
|
+
return _jsx("div", { className: classes, children: children });
|
|
44
25
|
};
|
|
45
26
|
export default Container;
|
|
@@ -1,36 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
3
|
import { useState, useEffect, useMemo } from "react";
|
|
3
4
|
import { X } from "lucide-react";
|
|
4
|
-
export const DrawerButton = ({ label = "Open Drawer", icon, iconPosition = "left", onClick,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
color: textColor,
|
|
10
|
-
border: "none",
|
|
11
|
-
borderRadius,
|
|
12
|
-
padding,
|
|
13
|
-
fontSize,
|
|
14
|
-
gap,
|
|
15
|
-
cursor: "pointer",
|
|
16
|
-
fontWeight: 500,
|
|
17
|
-
transition: "all 0.2s ease",
|
|
18
|
-
boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
|
|
19
|
-
...style,
|
|
20
|
-
}, className: className, children: [icon && iconPosition === "left" && icon, label, icon && iconPosition === "right" && icon] }));
|
|
21
|
-
export const Drawer = ({ open, onClose, position = "right", width = "320px", height = "320px", backgroundColor = "#fff", backdropColor = "rgba(0,0,0,0.5)", transitionDuration = 300, style, className = "", children, showCloseButton = true, closeIconColor = "#000", closeButtonStyle, }) => {
|
|
22
|
-
const [visible, setVisible] = useState(open);
|
|
23
|
-
// Handle mount/unmount delay for smooth fade-out
|
|
5
|
+
export const DrawerButton = ({ label = "Open Drawer", icon, iconPosition = "left", onClick, className = "", }) => {
|
|
6
|
+
return (_jsxs("button", { type: "button", onClick: onClick, className: `inline-flex items-center justify-center gap-2 px-4 py-2 rounded-md font-medium text-white bg-blue-600 hover:bg-blue-700 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-400 ${className}`, children: [icon && iconPosition === "left" && icon, label, icon && iconPosition === "right" && icon] }));
|
|
7
|
+
};
|
|
8
|
+
export const Drawer = ({ open, onClose, position = "right", size = "320px", children, showCloseButton = true, className = "", overlayClassName = "", closeButtonClassName = "", }) => {
|
|
9
|
+
const [mounted, setMounted] = useState(open);
|
|
24
10
|
useEffect(() => {
|
|
25
11
|
if (open)
|
|
26
|
-
|
|
12
|
+
setMounted(true);
|
|
27
13
|
else
|
|
28
|
-
setTimeout(() =>
|
|
29
|
-
}, [open
|
|
30
|
-
// Drawer transform direction
|
|
14
|
+
setTimeout(() => setMounted(false), 300); // smooth exit
|
|
15
|
+
}, [open]);
|
|
31
16
|
const transform = useMemo(() => {
|
|
32
17
|
if (open)
|
|
33
|
-
return "translate(0,
|
|
18
|
+
return "translate(0,0)";
|
|
34
19
|
switch (position) {
|
|
35
20
|
case "left":
|
|
36
21
|
return "translateX(-100%)";
|
|
@@ -41,54 +26,18 @@ export const Drawer = ({ open, onClose, position = "right", width = "320px", hei
|
|
|
41
26
|
case "bottom":
|
|
42
27
|
return "translateY(100%)";
|
|
43
28
|
default:
|
|
44
|
-
return "translate(0,
|
|
29
|
+
return "translate(0,0)";
|
|
45
30
|
}
|
|
46
31
|
}, [open, position]);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
};
|
|
59
|
-
const overlayStyle = {
|
|
60
|
-
position: "fixed",
|
|
61
|
-
inset: 0,
|
|
62
|
-
backgroundColor: backdropColor,
|
|
63
|
-
opacity: open ? 1 : 0,
|
|
64
|
-
transition: `opacity ${transitionDuration}ms ease`,
|
|
65
|
-
zIndex: 1000,
|
|
66
|
-
display: visible ? "block" : "none",
|
|
67
|
-
pointerEvents: open ? "auto" : "none",
|
|
68
|
-
};
|
|
69
|
-
const defaultCloseButtonStyle = {
|
|
70
|
-
position: "absolute",
|
|
71
|
-
top: "12px",
|
|
72
|
-
right: "12px",
|
|
73
|
-
background: "none",
|
|
74
|
-
border: "none",
|
|
75
|
-
cursor: "pointer",
|
|
76
|
-
display: "flex",
|
|
77
|
-
alignItems: "center",
|
|
78
|
-
justifyContent: "center",
|
|
79
|
-
transition: "transform 0.2s ease, opacity 0.2s ease",
|
|
80
|
-
};
|
|
81
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { style: overlayStyle, onClick: onClose }), _jsxs("div", { style: {
|
|
82
|
-
...drawerStyle,
|
|
83
|
-
display: "flex",
|
|
84
|
-
flexDirection: "column",
|
|
85
|
-
visibility: visible ? "visible" : "hidden",
|
|
86
|
-
pointerEvents: open ? "auto" : "none",
|
|
87
|
-
boxShadow: "0 0 20px rgba(0,0,0,0.15)",
|
|
88
|
-
}, className: className, children: [showCloseButton && (_jsx("button", { onClick: onClose, style: { ...defaultCloseButtonStyle, ...closeButtonStyle }, "aria-label": "Close drawer", children: _jsx(X, { size: 22, color: closeIconColor }) })), _jsx("div", { style: {
|
|
89
|
-
flex: 1,
|
|
90
|
-
overflowY: "auto",
|
|
91
|
-
padding: "16px",
|
|
92
|
-
scrollbarWidth: "thin",
|
|
93
|
-
}, children: children })] })] }));
|
|
32
|
+
if (!mounted && !open)
|
|
33
|
+
return null;
|
|
34
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { onClick: onClose, className: `fixed inset-0 bg-black/50 transition-opacity ${open ? "opacity-100" : "opacity-0"} ${overlayClassName}` }), _jsxs("div", { className: `fixed bg-white shadow-lg flex flex-col transition-transform duration-300 ${className}`, style: {
|
|
35
|
+
width: position === "left" || position === "right" ? size : "100%",
|
|
36
|
+
height: position === "top" || position === "bottom" ? size : "100%",
|
|
37
|
+
top: position === "bottom" || position === "top" ? (position === "bottom" ? "auto" : 0) : 0,
|
|
38
|
+
bottom: position === "bottom" ? 0 : "auto",
|
|
39
|
+
left: position === "left" ? 0 : position === "right" ? "auto" : 0,
|
|
40
|
+
right: position === "right" ? 0 : position === "left" ? "auto" : 0,
|
|
41
|
+
transform,
|
|
42
|
+
}, children: [showCloseButton && (_jsx("button", { onClick: onClose, className: `absolute top-4 right-4 p-1 rounded-full hover:bg-gray-200 transition-colors ${closeButtonClassName}`, "aria-label": "Close drawer", children: _jsx(X, { size: 20 }) })), _jsx("div", { className: "flex-1 overflow-auto p-4", children: children })] })] }));
|
|
94
43
|
};
|