@neuctra/ui 0.2.2 → 0.2.4
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 +27 -18
- package/dist/components/basic/Alert.d.ts +14 -2
- package/dist/components/basic/Avatar.d.ts +5 -3
- package/dist/components/basic/Badge.d.ts +3 -3
- package/dist/components/basic/Button.d.ts +15 -17
- package/dist/components/basic/Card.d.ts +7 -49
- package/dist/components/basic/{CheckRadioInput.d.ts → CheckboxGroup.d.ts} +4 -5
- package/dist/components/basic/Container.d.ts +28 -26
- package/dist/components/basic/Drawer.d.ts +20 -11
- package/dist/components/basic/DropDown.d.ts +24 -34
- package/dist/components/basic/Flexbox.d.ts +18 -10
- package/dist/components/basic/GridView.d.ts +7 -5
- package/dist/components/basic/Image.d.ts +31 -6
- package/dist/components/basic/Input.d.ts +18 -10
- package/dist/components/basic/List.d.ts +11 -3
- package/dist/components/basic/Modal.d.ts +8 -2
- package/dist/components/basic/RadioGroup.d.ts +25 -0
- package/dist/components/basic/Section.d.ts +36 -0
- package/dist/components/basic/Stack.d.ts +27 -0
- package/dist/components/basic/SwitchGroup.d.ts +25 -0
- package/dist/components/basic/Table.d.ts +18 -54
- package/dist/components/basic/Tabs.d.ts +28 -28
- package/dist/components/basic/Text.d.ts +19 -32
- package/dist/index.cjs.js +55 -176
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +19 -18
- package/dist/index.es.js +3501 -4738
- package/dist/index.es.js.map +1 -0
- package/dist/src/components/avatar/AvatarGroup.js +9 -0
- package/dist/src/components/avatar/AvatarWithStatus.js +18 -0
- package/dist/src/components/basic/Accordation.js +74 -0
- package/dist/src/components/basic/Alert.js +141 -0
- package/dist/src/components/basic/AudioGallery.js +425 -0
- package/dist/src/components/basic/AudioPlayer.js +116 -0
- package/dist/src/components/basic/Avatar.js +181 -0
- package/dist/src/components/basic/Badge.js +66 -0
- package/dist/src/components/basic/Button.js +101 -0
- package/dist/src/components/basic/Card.js +47 -0
- package/dist/src/components/basic/CheckboxGroup.js +40 -0
- package/dist/src/components/basic/Container.js +45 -0
- package/dist/src/components/basic/Drawer.js +94 -0
- package/dist/src/components/basic/DropDown.js +162 -0
- package/dist/src/components/basic/Flexbox.js +67 -0
- package/dist/src/components/basic/GridView.js +51 -0
- package/dist/src/components/basic/Image.js +95 -0
- package/dist/src/components/basic/Input.js +123 -0
- package/dist/src/components/basic/List.js +71 -0
- package/dist/src/components/basic/Modal.js +86 -0
- package/dist/src/components/basic/RadioGroup.js +37 -0
- package/dist/src/components/basic/Section.js +100 -0
- package/dist/src/components/basic/Stack.js +75 -0
- package/dist/src/components/basic/SwitchGroup.js +50 -0
- package/dist/src/components/basic/Table.js +32 -0
- package/dist/src/components/basic/Tabs.js +149 -0
- package/dist/src/components/basic/Text.js +117 -0
- package/dist/src/index.js +46 -0
- package/dist/types/src/components/basic/Accordation.d.ts +44 -0
- package/dist/types/{components → src/components}/basic/Alert.d.ts +14 -2
- package/dist/types/{components → src/components}/basic/Avatar.d.ts +5 -3
- package/dist/types/{components → src/components}/basic/Badge.d.ts +3 -3
- package/dist/types/src/components/basic/Button.d.ts +26 -0
- package/dist/types/src/components/basic/Card.d.ts +28 -0
- package/dist/types/{components/basic/CheckRadioInput.d.ts → src/components/basic/CheckboxGroup.d.ts} +4 -5
- package/dist/types/src/components/basic/Container.d.ts +32 -0
- package/dist/types/src/components/basic/Drawer.d.ts +33 -0
- package/dist/types/src/components/basic/DropDown.d.ts +53 -0
- package/dist/types/src/components/basic/Flexbox.d.ts +25 -0
- package/dist/types/{components → src/components}/basic/GridView.d.ts +7 -5
- package/dist/types/src/components/basic/Image.d.ts +58 -0
- package/dist/types/{components → src/components}/basic/Input.d.ts +18 -10
- package/dist/types/{components → src/components}/basic/List.d.ts +11 -3
- package/dist/types/{components → src/components}/basic/Modal.d.ts +8 -2
- package/dist/types/src/components/basic/RadioGroup.d.ts +25 -0
- package/dist/types/src/components/basic/Section.d.ts +36 -0
- package/dist/types/src/components/basic/Stack.d.ts +27 -0
- package/dist/types/src/components/basic/SwitchGroup.d.ts +25 -0
- package/dist/types/src/components/basic/Table.d.ts +23 -0
- package/dist/types/src/components/basic/Tabs.d.ts +47 -0
- package/dist/types/src/components/basic/Text.d.ts +26 -0
- package/dist/types/{index.d.ts → src/index.d.ts} +19 -18
- package/dist/types/vite.config.d.ts +2 -0
- package/dist/ui.css +1 -1
- package/dist/vite.config.js +34 -0
- package/package.json +2 -1
- package/dist/components/basic/ImageGallery.d.ts +0 -21
- package/dist/components/basic/VideoGallery.d.ts +0 -136
- package/dist/components/basic/VideoPlayer.d.ts +0 -36
- package/dist/types/components/basic/Accordation.d.ts +0 -35
- package/dist/types/components/basic/Button.d.ts +0 -28
- package/dist/types/components/basic/Card.d.ts +0 -70
- package/dist/types/components/basic/Container.d.ts +0 -30
- package/dist/types/components/basic/Drawer.d.ts +0 -24
- package/dist/types/components/basic/DropDown.d.ts +0 -63
- package/dist/types/components/basic/Flexbox.d.ts +0 -17
- package/dist/types/components/basic/Image.d.ts +0 -33
- package/dist/types/components/basic/ImageGallery.d.ts +0 -21
- package/dist/types/components/basic/Table.d.ts +0 -59
- package/dist/types/components/basic/Tabs.d.ts +0 -47
- package/dist/types/components/basic/Text.d.ts +0 -39
- package/dist/types/components/basic/VideoGallery.d.ts +0 -136
- package/dist/types/components/basic/VideoPlayer.d.ts +0 -36
- /package/dist/types/{components → src/components}/avatar/AvatarGroup.d.ts +0 -0
- /package/dist/types/{components → src/components}/avatar/AvatarWithStatus.d.ts +0 -0
- /package/dist/types/{components → src/components}/basic/AudioGallery.d.ts +0 -0
- /package/dist/types/{components → src/components}/basic/AudioPlayer.d.ts +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { User } from "lucide-react";
|
|
4
|
+
// --- Base maps ---
|
|
5
|
+
const sizeMap = {
|
|
6
|
+
xs: 24,
|
|
7
|
+
sm: 32,
|
|
8
|
+
md: 40,
|
|
9
|
+
lg: 48,
|
|
10
|
+
xl: 56,
|
|
11
|
+
"2xl": 64,
|
|
12
|
+
};
|
|
13
|
+
const fontSizeMap = {
|
|
14
|
+
xs: 10,
|
|
15
|
+
sm: 12,
|
|
16
|
+
md: 14,
|
|
17
|
+
lg: 16,
|
|
18
|
+
xl: 18,
|
|
19
|
+
"2xl": 20,
|
|
20
|
+
};
|
|
21
|
+
const statusSizeMap = {
|
|
22
|
+
xs: 6,
|
|
23
|
+
sm: 8,
|
|
24
|
+
md: 10,
|
|
25
|
+
lg: 12,
|
|
26
|
+
xl: 14,
|
|
27
|
+
"2xl": 16,
|
|
28
|
+
};
|
|
29
|
+
// --- Helpers ---
|
|
30
|
+
const getVariantStyles = (variant) => {
|
|
31
|
+
switch (variant) {
|
|
32
|
+
case "square":
|
|
33
|
+
return "0px";
|
|
34
|
+
case "rounded":
|
|
35
|
+
return "8px";
|
|
36
|
+
default:
|
|
37
|
+
return "50%";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const getStatusPositionStyle = (position, offset) => {
|
|
41
|
+
switch (position) {
|
|
42
|
+
case "top-left":
|
|
43
|
+
return { top: 2, left: 2 };
|
|
44
|
+
case "top-right":
|
|
45
|
+
return { top: 2, right: 2 };
|
|
46
|
+
case "bottom-left":
|
|
47
|
+
return { bottom: 2, left: 2 };
|
|
48
|
+
default:
|
|
49
|
+
return { bottom: 2, right: 2 };
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const getSpacingOffset = (spacing, dim) => {
|
|
53
|
+
switch (spacing) {
|
|
54
|
+
case "tight":
|
|
55
|
+
return -(dim * 0.5);
|
|
56
|
+
case "loose":
|
|
57
|
+
return -(dim * 0.15);
|
|
58
|
+
default:
|
|
59
|
+
return -(dim * 0.35);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
// --- Avatar Component ---
|
|
63
|
+
export const Avatar = ({ src, alt = "User avatar", size = "md", variant = "circular", isOnline = false, isOffline = false, className = "", style, statusClassName = "", statusStyle, statusPosition = "bottom-right", fallback, ring = false, ringColor = "#3b82f6", onClick, }) => {
|
|
64
|
+
const [imageError, setImageError] = useState(false);
|
|
65
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
66
|
+
const isResponsive = size === "responsive";
|
|
67
|
+
const dimension = !isResponsive ? sizeMap[size] : undefined;
|
|
68
|
+
const fontSize = !isResponsive ? fontSizeMap[size] : "clamp(10px, 2vw, 16px)";
|
|
69
|
+
const statusSize = !isResponsive ? statusSizeMap[size] : 10;
|
|
70
|
+
const borderRadius = getVariantStyles(variant);
|
|
71
|
+
const initials = fallback || alt
|
|
72
|
+
.split(" ")
|
|
73
|
+
.map((n) => n[0])
|
|
74
|
+
.join("")
|
|
75
|
+
.toUpperCase()
|
|
76
|
+
.slice(0, 2);
|
|
77
|
+
const statusColor = isOnline ? "#10b981" : isOffline ? "#6b7280" : "";
|
|
78
|
+
const statusLabel = isOnline ? "Online" : isOffline ? "Offline" : "";
|
|
79
|
+
const showImage = src && !imageError;
|
|
80
|
+
const clickable = !!onClick;
|
|
81
|
+
return (_jsxs("div", { className: className, role: clickable ? "button" : "img", tabIndex: clickable ? 0 : -1, "aria-label": alt, onClick: onClick, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onKeyDown: (e) => {
|
|
82
|
+
if (clickable && (e.key === "Enter" || e.key === " ")) {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
onClick?.();
|
|
85
|
+
}
|
|
86
|
+
}, style: {
|
|
87
|
+
position: "relative",
|
|
88
|
+
width: isResponsive ? "100%" : dimension,
|
|
89
|
+
height: isResponsive ? "100%" : dimension,
|
|
90
|
+
borderRadius,
|
|
91
|
+
overflow: "visible",
|
|
92
|
+
display: "inline-flex",
|
|
93
|
+
alignItems: "center",
|
|
94
|
+
justifyContent: "center",
|
|
95
|
+
flexShrink: 0,
|
|
96
|
+
cursor: clickable ? "pointer" : "default",
|
|
97
|
+
transition: "all 0.25s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
98
|
+
transform: isHovered && clickable ? "scale(1.05)" : "scale(1)",
|
|
99
|
+
boxShadow: ring
|
|
100
|
+
? `0 0 0 3px ${ringColor}22, 0 0 0 1px ${ringColor}`
|
|
101
|
+
: isHovered && clickable
|
|
102
|
+
? "0 6px 16px rgba(0, 0, 0, 0.15)"
|
|
103
|
+
: "0 1px 3px rgba(0, 0, 0, 0.1)",
|
|
104
|
+
...style,
|
|
105
|
+
}, children: [showImage ? (_jsx("img", { src: src, alt: alt, onError: () => setImageError(true), loading: "lazy", style: {
|
|
106
|
+
width: "100%",
|
|
107
|
+
height: "100%",
|
|
108
|
+
objectFit: "cover",
|
|
109
|
+
borderRadius,
|
|
110
|
+
transition: "opacity 0.3s ease-in-out",
|
|
111
|
+
} })) : (_jsx("div", { style: {
|
|
112
|
+
width: "100%",
|
|
113
|
+
height: "100%",
|
|
114
|
+
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
|
115
|
+
color: "#fff",
|
|
116
|
+
fontSize,
|
|
117
|
+
fontWeight: 600,
|
|
118
|
+
borderRadius,
|
|
119
|
+
display: "flex",
|
|
120
|
+
alignItems: "center",
|
|
121
|
+
justifyContent: "center",
|
|
122
|
+
}, children: initials || _jsx(User, { size: dimension ? dimension * 0.5 : 20 }) })), (isOnline || isOffline) && (_jsx("div", { className: statusClassName, "aria-label": statusLabel, title: statusLabel, style: {
|
|
123
|
+
position: "absolute",
|
|
124
|
+
width: statusSize,
|
|
125
|
+
height: statusSize,
|
|
126
|
+
borderRadius: "50%",
|
|
127
|
+
backgroundColor: statusColor,
|
|
128
|
+
border: "2px solid white",
|
|
129
|
+
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.12)",
|
|
130
|
+
...getStatusPositionStyle(statusPosition, statusSize),
|
|
131
|
+
...statusStyle,
|
|
132
|
+
} }))] }));
|
|
133
|
+
};
|
|
134
|
+
// --- AvatarGroup Component ---
|
|
135
|
+
export const AvatarGroup = ({ avatars, max = 4, size = "md", className = "", style, spacing = "normal", direction = "left", }) => {
|
|
136
|
+
const isResponsive = size === "responsive";
|
|
137
|
+
const dimension = !isResponsive ? sizeMap[size] : 40;
|
|
138
|
+
const fontSize = !isResponsive ? fontSizeMap[size] : "clamp(10px, 2vw, 14px)";
|
|
139
|
+
const spacingOffset = getSpacingOffset(spacing, dimension);
|
|
140
|
+
const visibleAvatars = avatars.slice(0, max);
|
|
141
|
+
const extraCount = avatars.length - max;
|
|
142
|
+
return (_jsxs("div", { className: className, style: {
|
|
143
|
+
display: "flex",
|
|
144
|
+
alignItems: "center",
|
|
145
|
+
flexDirection: direction === "right" ? "row-reverse" : "row",
|
|
146
|
+
flexWrap: "wrap",
|
|
147
|
+
...style,
|
|
148
|
+
}, role: "group", "aria-label": `Avatar group with ${avatars.length} members`, children: [visibleAvatars.map((avatar, i) => {
|
|
149
|
+
const isFirst = direction === "left" ? i === 0 : i === visibleAvatars.length - 1;
|
|
150
|
+
const zIndex = direction === "left" ? visibleAvatars.length - i : i + 1;
|
|
151
|
+
return (_jsx("div", { style: {
|
|
152
|
+
marginLeft: direction === "left" && !isFirst ? spacingOffset : 0,
|
|
153
|
+
marginRight: direction === "right" && !isFirst ? spacingOffset : 0,
|
|
154
|
+
zIndex,
|
|
155
|
+
position: "relative",
|
|
156
|
+
transition: "transform 0.25s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
157
|
+
}, onMouseEnter: (e) => {
|
|
158
|
+
e.currentTarget.style.transform = `translateY(-3px) scale(1.05)`;
|
|
159
|
+
e.currentTarget.style.zIndex = "100";
|
|
160
|
+
}, onMouseLeave: (e) => {
|
|
161
|
+
e.currentTarget.style.transform = "translateY(0) scale(1)";
|
|
162
|
+
e.currentTarget.style.zIndex = zIndex.toString();
|
|
163
|
+
}, children: _jsx(Avatar, { ...avatar, size: size }) }, i));
|
|
164
|
+
}), extraCount > 0 && (_jsxs("div", { style: {
|
|
165
|
+
marginLeft: direction === "left" ? spacingOffset : 0,
|
|
166
|
+
marginRight: direction === "right" ? spacingOffset : 0,
|
|
167
|
+
width: dimension,
|
|
168
|
+
height: dimension,
|
|
169
|
+
borderRadius: "50%",
|
|
170
|
+
background: "linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%)",
|
|
171
|
+
color: "#374151",
|
|
172
|
+
fontSize,
|
|
173
|
+
fontWeight: 600,
|
|
174
|
+
display: "flex",
|
|
175
|
+
justifyContent: "center",
|
|
176
|
+
alignItems: "center",
|
|
177
|
+
border: "3px solid white",
|
|
178
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
|
|
179
|
+
userSelect: "none",
|
|
180
|
+
}, title: `${extraCount} more members`, children: ["+", extraCount] }))] }));
|
|
181
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo } from "react";
|
|
3
|
+
export const Badge = memo(({ text, color = "#2563eb", textColor = "#fff", borderColor = "#2563eb", borderWidth = "0", icon, iconPosition = "left", rounded = false, borderRadius, fontSize = "13px", fontWeight = 500, horizontalPadding = "10px", verticalPadding = "4px", margin = "0", shadow = "0 1px 3px rgba(0,0,0,0.1)", notificationDot = false, dotColor = "#ef4444", count, pulse = false, style, className = "", onClick, }) => {
|
|
4
|
+
const baseStyle = {
|
|
5
|
+
display: "inline-flex",
|
|
6
|
+
alignItems: "center",
|
|
7
|
+
justifyContent: "center",
|
|
8
|
+
backgroundColor: color,
|
|
9
|
+
color: textColor,
|
|
10
|
+
border: `${borderWidth} solid ${borderColor}`,
|
|
11
|
+
borderRadius: borderRadius || (rounded ? "9999px" : "6px"),
|
|
12
|
+
padding: `${verticalPadding} ${horizontalPadding}`,
|
|
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,
|
|
23
|
+
};
|
|
24
|
+
const dotStyle = {
|
|
25
|
+
position: "absolute",
|
|
26
|
+
top: "-4px",
|
|
27
|
+
right: "-4px",
|
|
28
|
+
height: "8px",
|
|
29
|
+
width: "8px",
|
|
30
|
+
backgroundColor: dotColor,
|
|
31
|
+
borderRadius: "50%",
|
|
32
|
+
animation: pulse ? "pulseAnim 1.2s infinite" : undefined,
|
|
33
|
+
};
|
|
34
|
+
const countStyle = {
|
|
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
|
+
` })] }));
|
|
66
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo, useState } from "react";
|
|
4
|
+
/** 🌈 Default color palette */
|
|
5
|
+
const defaultColors = {
|
|
6
|
+
light: {
|
|
7
|
+
default: "#111",
|
|
8
|
+
primary: "#2563eb",
|
|
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]);
|
|
61
|
+
/** 📏 Sizes */
|
|
62
|
+
const sizes = {
|
|
63
|
+
sm: { px: 16, py: 6, font: "0.85rem" },
|
|
64
|
+
md: { px: 24, py: 10, font: "1rem" },
|
|
65
|
+
lg: { px: 32, py: 14, font: "1.125rem" },
|
|
66
|
+
};
|
|
67
|
+
const s = sizes[size];
|
|
68
|
+
/** 💅 Computed styles */
|
|
69
|
+
const buttonStyle = {
|
|
70
|
+
display: "inline-flex",
|
|
71
|
+
alignItems: "center",
|
|
72
|
+
justifyContent: "center",
|
|
73
|
+
gap: "8px",
|
|
74
|
+
padding: `${s.py}px ${s.px}px`,
|
|
75
|
+
fontSize: s.font,
|
|
76
|
+
fontWeight: 500,
|
|
77
|
+
borderRadius: rounded ? 8 : 3,
|
|
78
|
+
border: showBorder ? `1px solid ${theme.border}` : "none", // ✅ conditional border
|
|
79
|
+
width: fullWidth ? "100%" : "auto",
|
|
80
|
+
backgroundColor: hovered && !disabled ? theme.hover : theme.primary,
|
|
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;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, } from "react";
|
|
3
|
+
const CardInner = ({ as, children, className = "", variant = "elevated", background = "#fff", textColor = "#000", borderRadius = 12, border, boxShadow, hoverShadow, padding = 16, margin, width, height, style = {}, hoverStyle = {}, onClick, ...rest }, ref) => {
|
|
4
|
+
const Component = as || "div";
|
|
5
|
+
// Default styling for known variants; unknown variants can be customized
|
|
6
|
+
const baseVariantStyles = {
|
|
7
|
+
elevated: {
|
|
8
|
+
boxShadow: boxShadow || "0 4px 12px rgba(0,0,0,0.08)",
|
|
9
|
+
border: border || "none",
|
|
10
|
+
},
|
|
11
|
+
outline: {
|
|
12
|
+
border: border || "1px solid rgba(0,0,0,0.1)",
|
|
13
|
+
boxShadow: "none",
|
|
14
|
+
},
|
|
15
|
+
flat: {
|
|
16
|
+
border: "none",
|
|
17
|
+
boxShadow: "none",
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
const cardStyle = {
|
|
21
|
+
background,
|
|
22
|
+
color: textColor,
|
|
23
|
+
borderRadius,
|
|
24
|
+
padding,
|
|
25
|
+
margin,
|
|
26
|
+
width,
|
|
27
|
+
height,
|
|
28
|
+
transition: "all 0.25s ease",
|
|
29
|
+
cursor: onClick ? "pointer" : undefined,
|
|
30
|
+
boxSizing: "border-box",
|
|
31
|
+
...(baseVariantStyles[variant] || {}), // fallback if variant is custom
|
|
32
|
+
...style,
|
|
33
|
+
};
|
|
34
|
+
const handleMouseEnter = (e) => {
|
|
35
|
+
Object.assign(e.currentTarget.style, hoverShadow ? { boxShadow: hoverShadow } : hoverStyle);
|
|
36
|
+
};
|
|
37
|
+
const handleMouseLeave = (e) => {
|
|
38
|
+
Object.assign(e.currentTarget.style, {
|
|
39
|
+
...cardStyle,
|
|
40
|
+
...(baseVariantStyles[variant] || {}),
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
return (_jsx(Component, { ref: ref, className: `ui-card ${className}`, style: cardStyle, onClick: onClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, ...rest, children: children }));
|
|
44
|
+
};
|
|
45
|
+
const ForwardedCard = forwardRef(CardInner);
|
|
46
|
+
const Card = Object.assign(ForwardedCard, { displayName: "Card" });
|
|
47
|
+
export { Card };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
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, }) => {
|
|
4
|
+
const handleChange = (value) => {
|
|
5
|
+
if (!onChange)
|
|
6
|
+
return;
|
|
7
|
+
const updatedValues = selectedValues.includes(value)
|
|
8
|
+
? selectedValues.filter((v) => v !== value)
|
|
9
|
+
: [...selectedValues, value];
|
|
10
|
+
onChange(updatedValues);
|
|
11
|
+
};
|
|
12
|
+
return (_jsxs("div", { className: className, style: { display: "flex", flexDirection: "column", gap: 8, ...style }, role: "group", "aria-disabled": disabled, children: [options.map((option) => {
|
|
13
|
+
const isChecked = selectedValues.includes(option.value);
|
|
14
|
+
return (_jsxs("label", { style: {
|
|
15
|
+
display: "flex",
|
|
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: {
|
|
24
|
+
display: "inline-flex",
|
|
25
|
+
justifyContent: "center",
|
|
26
|
+
alignItems: "center",
|
|
27
|
+
width: iconSize,
|
|
28
|
+
height: iconSize,
|
|
29
|
+
borderRadius: 4,
|
|
30
|
+
border: `2px solid ${isChecked ? iconCheckedBgColor : iconUncheckedBorderColor}`,
|
|
31
|
+
backgroundColor: isChecked ? iconCheckedBgColor : "transparent",
|
|
32
|
+
transition: "all 0.25s ease",
|
|
33
|
+
}, children: isChecked && (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "white", strokeWidth: 3, strokeLinecap: "round", strokeLinejoin: "round", style: { width: iconSize * 0.6, height: iconSize * 0.6 }, children: _jsx("polyline", { points: "20 6 9 17 4 12" }) })) }))] }, option.value));
|
|
34
|
+
}), error && (_jsx("p", { role: "alert", style: {
|
|
35
|
+
color: "#dc2626",
|
|
36
|
+
fontSize: 12,
|
|
37
|
+
marginTop: 4,
|
|
38
|
+
...errorStyle,
|
|
39
|
+
}, children: error }))] }));
|
|
40
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
/** Tailwind-like container widths */
|
|
4
|
+
const containerWidths = {
|
|
5
|
+
sm: "640px",
|
|
6
|
+
md: "768px",
|
|
7
|
+
lg: "1024px",
|
|
8
|
+
xl: "1280px",
|
|
9
|
+
"2xl": "1536px",
|
|
10
|
+
full: "100%",
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* 🧱 Container Component
|
|
14
|
+
* A flexible, responsive layout wrapper inspired by Tailwind's container.
|
|
15
|
+
*/
|
|
16
|
+
export const Container = ({ size = "lg", padding = 0, margin = "0 auto", backgroundColor = "transparent", center = true, width, height = "auto", borderRadius = 0, style, className = "", children, }) => {
|
|
17
|
+
const styles = useMemo(() => {
|
|
18
|
+
const computedPadding = typeof padding === "number" ? `${padding}px` : padding;
|
|
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 }));
|
|
44
|
+
};
|
|
45
|
+
export default Container;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useMemo } from "react";
|
|
3
|
+
import { X } from "lucide-react";
|
|
4
|
+
export const DrawerButton = ({ label = "Open Drawer", icon, iconPosition = "left", onClick, color = "#2563eb", textColor = "#fff", borderRadius = "6px", padding = "10px 16px", fontSize = "14px", gap = "8px", style, className = "", }) => (_jsxs("button", { onClick: onClick, style: {
|
|
5
|
+
display: "inline-flex",
|
|
6
|
+
alignItems: "center",
|
|
7
|
+
justifyContent: "center",
|
|
8
|
+
backgroundColor: color,
|
|
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
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (open)
|
|
26
|
+
setVisible(true);
|
|
27
|
+
else
|
|
28
|
+
setTimeout(() => setVisible(false), transitionDuration);
|
|
29
|
+
}, [open, transitionDuration]);
|
|
30
|
+
// Drawer transform direction
|
|
31
|
+
const transform = useMemo(() => {
|
|
32
|
+
if (open)
|
|
33
|
+
return "translate(0, 0)";
|
|
34
|
+
switch (position) {
|
|
35
|
+
case "left":
|
|
36
|
+
return "translateX(-100%)";
|
|
37
|
+
case "right":
|
|
38
|
+
return "translateX(100%)";
|
|
39
|
+
case "top":
|
|
40
|
+
return "translateY(-100%)";
|
|
41
|
+
case "bottom":
|
|
42
|
+
return "translateY(100%)";
|
|
43
|
+
default:
|
|
44
|
+
return "translate(0, 0)";
|
|
45
|
+
}
|
|
46
|
+
}, [open, position]);
|
|
47
|
+
const drawerStyle = {
|
|
48
|
+
position: "fixed",
|
|
49
|
+
backgroundColor,
|
|
50
|
+
transition: `transform ${transitionDuration}ms ease, opacity ${transitionDuration}ms ease`,
|
|
51
|
+
transform,
|
|
52
|
+
opacity: open ? 1 : 0,
|
|
53
|
+
zIndex: 1001,
|
|
54
|
+
...style,
|
|
55
|
+
...(position === "left" || position === "right"
|
|
56
|
+
? { top: 0, bottom: 0, [position]: 0, width, height: "100%" }
|
|
57
|
+
: { left: 0, right: 0, [position]: 0, height, width: "100%" }),
|
|
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 })] })] }));
|
|
94
|
+
};
|