@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.
Files changed (98) hide show
  1. package/dist/components/basic/Accordation.d.ts +27 -18
  2. package/dist/components/basic/Alert.d.ts +15 -2
  3. package/dist/components/basic/Avatar.d.ts +5 -3
  4. package/dist/components/basic/Badge.d.ts +3 -3
  5. package/dist/components/basic/Button.d.ts +15 -17
  6. package/dist/components/basic/Card.d.ts +7 -49
  7. package/dist/components/basic/CheckRadioInput.d.ts +3 -1
  8. package/dist/components/basic/Container.d.ts +28 -26
  9. package/dist/components/basic/Drawer.d.ts +20 -11
  10. package/dist/components/basic/Flexbox.d.ts +18 -10
  11. package/dist/components/basic/GridView.d.ts +7 -5
  12. package/dist/components/basic/Image.d.ts +31 -6
  13. package/dist/components/basic/Input.d.ts +18 -10
  14. package/dist/components/basic/List.d.ts +11 -3
  15. package/dist/components/basic/Modal.d.ts +15 -2
  16. package/dist/components/basic/Section.d.ts +36 -0
  17. package/dist/components/basic/Stack.d.ts +27 -0
  18. package/dist/components/basic/Table.d.ts +18 -54
  19. package/dist/components/basic/Tabs.d.ts +28 -28
  20. package/dist/components/basic/Text.d.ts +19 -32
  21. package/dist/index.cjs.js +68 -223
  22. package/dist/index.cjs.js.map +1 -0
  23. package/dist/index.d.ts +17 -19
  24. package/dist/index.es.js +3169 -6313
  25. package/dist/index.es.js.map +1 -0
  26. package/dist/src/components/avatar/AvatarGroup.js +9 -0
  27. package/dist/src/components/avatar/AvatarWithStatus.js +18 -0
  28. package/dist/src/components/basic/Accordation.js +74 -0
  29. package/dist/src/components/basic/Alert.js +126 -0
  30. package/dist/src/components/basic/AudioGallery.js +425 -0
  31. package/dist/src/components/basic/AudioPlayer.js +116 -0
  32. package/dist/src/components/basic/Avatar.js +181 -0
  33. package/dist/src/components/basic/Badge.js +66 -0
  34. package/dist/src/components/basic/Button.js +101 -0
  35. package/dist/src/components/basic/Card.js +45 -0
  36. package/dist/src/components/basic/CheckRadioInput.js +83 -0
  37. package/dist/src/components/basic/Container.js +45 -0
  38. package/dist/src/components/basic/Drawer.js +94 -0
  39. package/dist/src/components/basic/DropDown.js +316 -0
  40. package/dist/src/components/basic/Flexbox.js +67 -0
  41. package/dist/src/components/basic/GridView.js +51 -0
  42. package/dist/src/components/basic/Image.js +95 -0
  43. package/dist/src/components/basic/Input.js +123 -0
  44. package/dist/src/components/basic/List.js +71 -0
  45. package/dist/src/components/basic/Modal.js +88 -0
  46. package/dist/src/components/basic/Section.js +100 -0
  47. package/dist/src/components/basic/Stack.js +75 -0
  48. package/dist/src/components/basic/Table.js +32 -0
  49. package/dist/src/components/basic/Tabs.js +149 -0
  50. package/dist/src/components/basic/Text.js +117 -0
  51. package/dist/src/index.js +44 -0
  52. package/dist/types/src/components/basic/Accordation.d.ts +44 -0
  53. package/dist/types/{components → src/components}/basic/Alert.d.ts +15 -2
  54. package/dist/types/{components → src/components}/basic/Avatar.d.ts +5 -3
  55. package/dist/types/{components → src/components}/basic/Badge.d.ts +3 -3
  56. package/dist/types/src/components/basic/Button.d.ts +26 -0
  57. package/dist/types/src/components/basic/Card.d.ts +28 -0
  58. package/dist/types/{components → src/components}/basic/CheckRadioInput.d.ts +3 -1
  59. package/dist/types/src/components/basic/Container.d.ts +32 -0
  60. package/dist/types/src/components/basic/Drawer.d.ts +33 -0
  61. package/dist/types/src/components/basic/Flexbox.d.ts +25 -0
  62. package/dist/types/{components → src/components}/basic/GridView.d.ts +7 -5
  63. package/dist/types/src/components/basic/Image.d.ts +58 -0
  64. package/dist/types/{components → src/components}/basic/Input.d.ts +18 -10
  65. package/dist/types/{components → src/components}/basic/List.d.ts +11 -3
  66. package/dist/types/src/components/basic/Modal.d.ts +24 -0
  67. package/dist/types/src/components/basic/Section.d.ts +36 -0
  68. package/dist/types/src/components/basic/Stack.d.ts +27 -0
  69. package/dist/types/src/components/basic/Table.d.ts +23 -0
  70. package/dist/types/src/components/basic/Tabs.d.ts +47 -0
  71. package/dist/types/src/components/basic/Text.d.ts +26 -0
  72. package/dist/types/{index.d.ts → src/index.d.ts} +17 -19
  73. package/dist/types/vite.config.d.ts +2 -0
  74. package/dist/ui.css +1 -1
  75. package/dist/vite.config.js +34 -0
  76. package/package.json +2 -1
  77. package/dist/components/basic/ImageGallery.d.ts +0 -21
  78. package/dist/components/basic/VideoGallery.d.ts +0 -136
  79. package/dist/components/basic/VideoPlayer.d.ts +0 -36
  80. package/dist/types/components/basic/Accordation.d.ts +0 -35
  81. package/dist/types/components/basic/Button.d.ts +0 -28
  82. package/dist/types/components/basic/Card.d.ts +0 -70
  83. package/dist/types/components/basic/Container.d.ts +0 -30
  84. package/dist/types/components/basic/Drawer.d.ts +0 -24
  85. package/dist/types/components/basic/Flexbox.d.ts +0 -17
  86. package/dist/types/components/basic/Image.d.ts +0 -33
  87. package/dist/types/components/basic/ImageGallery.d.ts +0 -21
  88. package/dist/types/components/basic/Modal.d.ts +0 -11
  89. package/dist/types/components/basic/Table.d.ts +0 -59
  90. package/dist/types/components/basic/Tabs.d.ts +0 -47
  91. package/dist/types/components/basic/Text.d.ts +0 -39
  92. package/dist/types/components/basic/VideoGallery.d.ts +0 -136
  93. package/dist/types/components/basic/VideoPlayer.d.ts +0 -36
  94. /package/dist/types/{components → src/components}/avatar/AvatarGroup.d.ts +0 -0
  95. /package/dist/types/{components → src/components}/avatar/AvatarWithStatus.d.ts +0 -0
  96. /package/dist/types/{components → src/components}/basic/AudioGallery.d.ts +0 -0
  97. /package/dist/types/{components → src/components}/basic/AudioPlayer.d.ts +0 -0
  98. /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;