@neuctra/ui 0.2.1 → 0.2.3
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 +15 -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 +3 -1
- package/dist/components/basic/Container.d.ts +28 -26
- package/dist/components/basic/Drawer.d.ts +20 -11
- 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 +15 -2
- package/dist/components/basic/Section.d.ts +36 -0
- package/dist/components/basic/Stack.d.ts +27 -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 +68 -223
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +17 -19
- package/dist/index.es.js +3169 -6313
- 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 +126 -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 +45 -0
- package/dist/src/components/basic/CheckRadioInput.js +83 -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 +316 -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 +88 -0
- package/dist/src/components/basic/Section.js +100 -0
- package/dist/src/components/basic/Stack.js +75 -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 +44 -0
- package/dist/types/src/components/basic/Accordation.d.ts +44 -0
- package/dist/types/{components → src/components}/basic/Alert.d.ts +15 -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 → src/components}/basic/CheckRadioInput.d.ts +3 -1
- 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/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/src/components/basic/Modal.d.ts +24 -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/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} +17 -19
- 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/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/Modal.d.ts +0 -11
- 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
- /package/dist/types/{components → src/components}/basic/DropDown.d.ts +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useState, useEffect } from "react";
|
|
3
|
+
import { Play, Pause, Volume2, VolumeX, Maximize, Minimize, RotateCcw, SkipBack, SkipForward, } from "lucide-react";
|
|
4
|
+
export const AudioPlayer = ({ src, thumbnail, autoPlay = false, loop = false, backgroundColor = "#000000", primaryColor = "#10b981", secondaryColor = "#ffffff", borderRadius = "12px", padding = "16px", width = "100%", className, }) => {
|
|
5
|
+
const audioRef = useRef(null);
|
|
6
|
+
const playerRef = useRef(null);
|
|
7
|
+
const [isPlaying, setIsPlaying] = useState(autoPlay);
|
|
8
|
+
const [currentTime, setCurrentTime] = useState(0);
|
|
9
|
+
const [duration, setDuration] = useState(0);
|
|
10
|
+
const [volume, setVolume] = useState(0.5);
|
|
11
|
+
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
12
|
+
const [isLooping, setIsLooping] = useState(loop);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (audioRef.current)
|
|
15
|
+
audioRef.current.volume = volume;
|
|
16
|
+
}, [volume]);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (audioRef.current)
|
|
19
|
+
audioRef.current.loop = isLooping;
|
|
20
|
+
}, [isLooping]);
|
|
21
|
+
const togglePlayPause = () => {
|
|
22
|
+
if (!audioRef.current)
|
|
23
|
+
return;
|
|
24
|
+
isPlaying ? audioRef.current.pause() : audioRef.current.play();
|
|
25
|
+
setIsPlaying(!isPlaying);
|
|
26
|
+
};
|
|
27
|
+
const handleTimeUpdate = () => {
|
|
28
|
+
if (!audioRef.current)
|
|
29
|
+
return;
|
|
30
|
+
setCurrentTime(audioRef.current.currentTime);
|
|
31
|
+
setDuration(audioRef.current.duration);
|
|
32
|
+
};
|
|
33
|
+
const handleSeek = (e) => {
|
|
34
|
+
if (!audioRef.current || !e.currentTarget)
|
|
35
|
+
return;
|
|
36
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
37
|
+
const percent = (e.clientX - rect.left) / rect.width;
|
|
38
|
+
const time = percent * duration;
|
|
39
|
+
audioRef.current.currentTime = time;
|
|
40
|
+
setCurrentTime(time);
|
|
41
|
+
};
|
|
42
|
+
const skip = (seconds) => {
|
|
43
|
+
if (audioRef.current)
|
|
44
|
+
audioRef.current.currentTime += seconds;
|
|
45
|
+
};
|
|
46
|
+
const toggleFullscreen = () => {
|
|
47
|
+
if (!playerRef.current)
|
|
48
|
+
return;
|
|
49
|
+
isFullscreen
|
|
50
|
+
? document.exitFullscreen?.()
|
|
51
|
+
: playerRef.current.requestFullscreen?.();
|
|
52
|
+
setIsFullscreen(!isFullscreen);
|
|
53
|
+
};
|
|
54
|
+
const formatTime = (time) => {
|
|
55
|
+
const minutes = Math.floor(time / 60);
|
|
56
|
+
const seconds = Math.floor(time % 60);
|
|
57
|
+
return `${minutes}:${seconds < 10 ? "0" : ""}${seconds}`;
|
|
58
|
+
};
|
|
59
|
+
return (_jsxs("div", { ref: playerRef, className: className, style: {
|
|
60
|
+
position: "relative",
|
|
61
|
+
width,
|
|
62
|
+
backgroundColor,
|
|
63
|
+
borderRadius,
|
|
64
|
+
color: secondaryColor,
|
|
65
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
|
|
66
|
+
overflow: "hidden",
|
|
67
|
+
padding,
|
|
68
|
+
boxSizing: "border-box",
|
|
69
|
+
maxWidth: "100%",
|
|
70
|
+
}, children: [_jsx("audio", { ref: audioRef, src: src, autoPlay: autoPlay, loop: loop, onTimeUpdate: handleTimeUpdate, onLoadedMetadata: handleTimeUpdate }), thumbnail && (_jsx("img", { src: thumbnail, alt: "Audio Thumbnail", style: {
|
|
71
|
+
width: "100%",
|
|
72
|
+
objectFit: "cover",
|
|
73
|
+
borderRadius,
|
|
74
|
+
marginBottom: "16px",
|
|
75
|
+
maxHeight: "150px",
|
|
76
|
+
} })), _jsxs("div", { style: {
|
|
77
|
+
display: "flex",
|
|
78
|
+
alignItems: "center",
|
|
79
|
+
justifyContent: "center",
|
|
80
|
+
gap: "10px",
|
|
81
|
+
flexShrink: 0,
|
|
82
|
+
marginTop: "10px",
|
|
83
|
+
}, children: [_jsx("button", { onClick: () => skip(-10), "aria-label": "Skip Back 10s", children: _jsx(SkipBack, { size: 20 }) }), _jsx("button", { onClick: togglePlayPause, style: {
|
|
84
|
+
background: primaryColor,
|
|
85
|
+
borderRadius: "9999px",
|
|
86
|
+
padding: "10px",
|
|
87
|
+
color: "#fff",
|
|
88
|
+
border: "none",
|
|
89
|
+
cursor: "pointer",
|
|
90
|
+
}, "aria-label": isPlaying ? "Pause" : "Play", children: isPlaying ? _jsx(Pause, { size: 20 }) : _jsx(Play, { size: 20 }) }), _jsx("button", { onClick: () => skip(10), "aria-label": "Skip Forward 10s", children: _jsx(SkipForward, { size: 20 }) })] }), _jsxs("div", { style: {
|
|
91
|
+
display: "flex",
|
|
92
|
+
flexWrap: "wrap",
|
|
93
|
+
alignItems: "center",
|
|
94
|
+
justifyContent: "space-between",
|
|
95
|
+
gap: "12px",
|
|
96
|
+
width: "100%",
|
|
97
|
+
}, children: [_jsxs("div", { style: { flexShrink: 0, minWidth: "60px", textAlign: "center" }, children: [_jsx("span", { style: { fontSize: "14px", marginRight: "4px" }, children: formatTime(currentTime) }), _jsxs("span", { style: { fontSize: "14px", color: "#aaa" }, children: ["/ ", formatTime(duration)] })] }), _jsx("div", { onClick: handleSeek, style: {
|
|
98
|
+
flex: 1,
|
|
99
|
+
height: "8px",
|
|
100
|
+
background: "#444",
|
|
101
|
+
borderRadius: "4px",
|
|
102
|
+
cursor: "pointer",
|
|
103
|
+
position: "relative",
|
|
104
|
+
minWidth: "100px",
|
|
105
|
+
}, children: _jsx("div", { style: {
|
|
106
|
+
width: `${(currentTime / duration) * 100 || 0}%`,
|
|
107
|
+
height: "100%",
|
|
108
|
+
background: primaryColor,
|
|
109
|
+
borderRadius: "4px",
|
|
110
|
+
} }) }), _jsxs("div", { style: {
|
|
111
|
+
display: "flex",
|
|
112
|
+
alignItems: "center",
|
|
113
|
+
gap: "10px",
|
|
114
|
+
flexShrink: 0,
|
|
115
|
+
}, children: [_jsx("button", { onClick: () => setIsLooping(!isLooping), "aria-label": "Toggle Loop", children: _jsx(RotateCcw, { size: 18, color: isLooping ? primaryColor : undefined }) }), _jsx("button", { onClick: () => setVolume(volume > 0 ? 0 : 0.5), "aria-label": "Toggle Mute", children: volume > 0 ? _jsx(Volume2, { size: 18 }) : _jsx(VolumeX, { size: 18 }) }), _jsx("button", { onClick: toggleFullscreen, "aria-label": "Toggle Fullscreen", children: isFullscreen ? _jsx(Minimize, { size: 18 }) : _jsx(Maximize, { size: 18 }) })] })] })] }));
|
|
116
|
+
};
|
|
@@ -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,45 @@
|
|
|
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 based on variant
|
|
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],
|
|
32
|
+
...style,
|
|
33
|
+
};
|
|
34
|
+
return (_jsx(Component, { ref: ref, className: `ui-card ${className}`, style: cardStyle, onClick: onClick, onMouseEnter: (e) => {
|
|
35
|
+
Object.assign(e.currentTarget.style, hoverShadow ? { boxShadow: hoverShadow } : hoverStyle);
|
|
36
|
+
}, onMouseLeave: (e) => {
|
|
37
|
+
Object.assign(e.currentTarget.style, {
|
|
38
|
+
...baseVariantStyles[variant],
|
|
39
|
+
...style,
|
|
40
|
+
});
|
|
41
|
+
}, ...rest, children: children }));
|
|
42
|
+
};
|
|
43
|
+
const ForwardedCard = forwardRef(CardInner);
|
|
44
|
+
const Card = Object.assign(ForwardedCard, { displayName: "Card" });
|
|
45
|
+
export { Card };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export const CheckRadio = ({ type = "checkbox", name, options, selectedValues, onChange, disabled = false, readOnly = false, required = false, error, className = "", customIcon, style, labelStyle, iconSize = 20, iconCheckedBgColor = "#2563eb", // blue-600
|
|
3
|
+
iconUncheckedBorderColor = "#9ca3af", // gray-400
|
|
4
|
+
switchBgColor = "#d1d5db", // gray-300
|
|
5
|
+
textColor = "#374151", // gray-700
|
|
6
|
+
errorStyle, }) => {
|
|
7
|
+
const isCheckbox = type === "checkbox";
|
|
8
|
+
const isRadio = type === "radio";
|
|
9
|
+
const isSwitch = type === "switch";
|
|
10
|
+
const handleChange = (value) => {
|
|
11
|
+
if (!onChange)
|
|
12
|
+
return;
|
|
13
|
+
if (isCheckbox) {
|
|
14
|
+
const updatedValues = Array.isArray(selectedValues)
|
|
15
|
+
? selectedValues.includes(value)
|
|
16
|
+
? selectedValues.filter((v) => v !== value)
|
|
17
|
+
: [...selectedValues, value]
|
|
18
|
+
: [value];
|
|
19
|
+
onChange(updatedValues);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
onChange(value);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
return (_jsxs("div", { className: className, style: {
|
|
26
|
+
display: "flex",
|
|
27
|
+
flexDirection: "column",
|
|
28
|
+
gap: 8,
|
|
29
|
+
...style,
|
|
30
|
+
}, role: type, "aria-disabled": disabled, children: [options.map((option) => {
|
|
31
|
+
const isChecked = isCheckbox
|
|
32
|
+
? Array.isArray(selectedValues) && selectedValues.includes(option.value)
|
|
33
|
+
: selectedValues === option.value;
|
|
34
|
+
return (_jsxs("label", { style: {
|
|
35
|
+
display: "flex",
|
|
36
|
+
alignItems: "center",
|
|
37
|
+
justifyContent: "space-between",
|
|
38
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
39
|
+
opacity: disabled ? 0.6 : 1,
|
|
40
|
+
gap: 8,
|
|
41
|
+
userSelect: "none",
|
|
42
|
+
...labelStyle,
|
|
43
|
+
}, children: [_jsx("span", { style: { color: textColor, fontSize: 14 }, children: option.label }), _jsx("input", { type: isSwitch ? "checkbox" : type, name: name, value: option.value, checked: isChecked, disabled: disabled || readOnly, required: required, onChange: () => handleChange(option.value), style: { display: "none" } }), isSwitch ? (_jsx("span", { style: {
|
|
44
|
+
position: "relative",
|
|
45
|
+
width: iconSize * 2,
|
|
46
|
+
height: iconSize * 1.1,
|
|
47
|
+
borderRadius: 9999,
|
|
48
|
+
backgroundColor: isChecked ? iconCheckedBgColor : switchBgColor,
|
|
49
|
+
transition: "background 0.2s ease",
|
|
50
|
+
flexShrink: 0,
|
|
51
|
+
}, children: _jsx("span", { style: {
|
|
52
|
+
position: "absolute",
|
|
53
|
+
top: 2,
|
|
54
|
+
left: isChecked ? iconSize : 2,
|
|
55
|
+
width: iconSize - 4,
|
|
56
|
+
height: iconSize - 4,
|
|
57
|
+
borderRadius: "50%",
|
|
58
|
+
backgroundColor: "#fff",
|
|
59
|
+
transition: "left 0.2s ease",
|
|
60
|
+
} }) })) : customIcon ? (customIcon(isChecked)) : (_jsxs("span", { style: {
|
|
61
|
+
display: "inline-flex",
|
|
62
|
+
justifyContent: "center",
|
|
63
|
+
alignItems: "center",
|
|
64
|
+
width: iconSize,
|
|
65
|
+
height: iconSize,
|
|
66
|
+
borderRadius: isCheckbox ? 4 : "50%",
|
|
67
|
+
border: `2px solid ${isChecked ? iconCheckedBgColor : iconUncheckedBorderColor}`,
|
|
68
|
+
backgroundColor: isChecked ? iconCheckedBgColor : "transparent",
|
|
69
|
+
transition: "all 0.2s ease",
|
|
70
|
+
flexShrink: 0,
|
|
71
|
+
}, children: [isChecked && isRadio && (_jsx("span", { style: {
|
|
72
|
+
width: iconSize / 2,
|
|
73
|
+
height: iconSize / 2,
|
|
74
|
+
borderRadius: "50%",
|
|
75
|
+
backgroundColor: "white",
|
|
76
|
+
} })), isChecked && isCheckbox && (_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));
|
|
77
|
+
}), error && (_jsx("p", { role: "alert", style: {
|
|
78
|
+
color: "#dc2626",
|
|
79
|
+
fontSize: 12,
|
|
80
|
+
marginTop: 4,
|
|
81
|
+
...errorStyle,
|
|
82
|
+
}, children: error }))] }));
|
|
83
|
+
};
|
|
@@ -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;
|