@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,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
export const RadioGroup = ({ name, options, selectedValue, onChange, disabled = false, readOnly = false, required = false, error, className = "", style, labelStyle, iconSize = 20, iconCheckedBgColor = "#2563eb", iconUncheckedBorderColor = "#9ca3af", textColor = "#374151", errorStyle, }) => {
|
|
4
|
+
return (_jsxs("div", { className: className, style: { display: "flex", flexDirection: "column", gap: 8, ...style }, role: "radiogroup", "aria-disabled": disabled, children: [options.map((option) => {
|
|
5
|
+
const isChecked = selectedValue === option.value;
|
|
6
|
+
return (_jsxs("label", { style: {
|
|
7
|
+
display: "flex",
|
|
8
|
+
alignItems: "center",
|
|
9
|
+
justifyContent: "space-between",
|
|
10
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
11
|
+
opacity: disabled ? 0.6 : 1,
|
|
12
|
+
gap: 8,
|
|
13
|
+
userSelect: "none",
|
|
14
|
+
...labelStyle,
|
|
15
|
+
}, children: [_jsx("span", { style: { color: textColor, fontSize: 14 }, children: option.label }), _jsx("input", { type: "radio", name: name, value: option.value, checked: isChecked, disabled: disabled || readOnly, required: required, onChange: () => onChange && onChange(option.value), style: { display: "none" } }), _jsx("span", { style: {
|
|
16
|
+
display: "inline-flex",
|
|
17
|
+
justifyContent: "center",
|
|
18
|
+
alignItems: "center",
|
|
19
|
+
width: iconSize,
|
|
20
|
+
height: iconSize,
|
|
21
|
+
borderRadius: "50%",
|
|
22
|
+
border: `2px solid ${isChecked ? iconCheckedBgColor : iconUncheckedBorderColor}`,
|
|
23
|
+
backgroundColor: isChecked ? iconCheckedBgColor : "transparent",
|
|
24
|
+
transition: "all 0.25s ease",
|
|
25
|
+
}, children: isChecked && (_jsx("span", { style: {
|
|
26
|
+
width: iconSize / 2,
|
|
27
|
+
height: iconSize / 2,
|
|
28
|
+
borderRadius: "50%",
|
|
29
|
+
backgroundColor: "white",
|
|
30
|
+
} })) })] }, option.value));
|
|
31
|
+
}), error && (_jsx("p", { role: "alert", style: {
|
|
32
|
+
color: "#dc2626",
|
|
33
|
+
fontSize: 12,
|
|
34
|
+
marginTop: 4,
|
|
35
|
+
...errorStyle,
|
|
36
|
+
}, children: error }))] }));
|
|
37
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useMemo } from "react";
|
|
3
|
+
import { Container } from "./Container";
|
|
4
|
+
/** 🎨 Preset Variants */
|
|
5
|
+
const variantStyles = {
|
|
6
|
+
light: { background: "#ffffff", color: "#111827" },
|
|
7
|
+
dark: { background: "#0f172a", color: "#f9fafb" },
|
|
8
|
+
gradient: {
|
|
9
|
+
background: "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)",
|
|
10
|
+
color: "#ffffff",
|
|
11
|
+
},
|
|
12
|
+
transparent: { background: "transparent", color: "#111827" },
|
|
13
|
+
primary: { background: "#3b82f6", color: "#ffffff" },
|
|
14
|
+
};
|
|
15
|
+
/** 🧩 Utility: Resolve responsive props safely */
|
|
16
|
+
function resolveResponsive(prop, screen, fallback) {
|
|
17
|
+
if (prop == null)
|
|
18
|
+
return fallback;
|
|
19
|
+
if (typeof prop !== "object")
|
|
20
|
+
return prop;
|
|
21
|
+
return prop[screen] ?? fallback;
|
|
22
|
+
}
|
|
23
|
+
/** 📱 Determine current breakpoint */
|
|
24
|
+
function getScreenSize(width) {
|
|
25
|
+
if (width < 768)
|
|
26
|
+
return "sm";
|
|
27
|
+
if (width < 1024)
|
|
28
|
+
return "md";
|
|
29
|
+
return "lg";
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 🧱 Section Component
|
|
33
|
+
* A flexible layout wrapper that supports responsive backgrounds,
|
|
34
|
+
* adaptive padding, and variant-based theming.
|
|
35
|
+
*/
|
|
36
|
+
export const Section = ({ title, subtitle, variant = "light", background, overlay, color, paddingY = 60, paddingX = 20, size = "xl", height = "auto", align = "center", borderRadius = 0, className = "", style, children, }) => {
|
|
37
|
+
const [screenSize, setScreenSize] = useState("lg");
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const updateSize = () => setScreenSize(getScreenSize(window.innerWidth));
|
|
40
|
+
updateSize();
|
|
41
|
+
window.addEventListener("resize", updateSize);
|
|
42
|
+
return () => window.removeEventListener("resize", updateSize);
|
|
43
|
+
}, []);
|
|
44
|
+
const resolvedVariant = resolveResponsive(variant, screenSize, "light");
|
|
45
|
+
const theme = variantStyles[resolvedVariant];
|
|
46
|
+
const resolvedStyles = useMemo(() => {
|
|
47
|
+
const bg = resolveResponsive(background, screenSize, theme.background);
|
|
48
|
+
const textColor = resolveResponsive(color, screenSize, theme.color);
|
|
49
|
+
const overlayColor = resolveResponsive(overlay, screenSize);
|
|
50
|
+
const py = resolveResponsive(paddingY, screenSize, 60);
|
|
51
|
+
const px = resolveResponsive(paddingX, screenSize, 20);
|
|
52
|
+
const alignText = resolveResponsive(align, screenSize, "center");
|
|
53
|
+
const h = resolveResponsive(height, screenSize, "auto");
|
|
54
|
+
const radius = resolveResponsive(borderRadius, screenSize, 0);
|
|
55
|
+
return {
|
|
56
|
+
position: "relative",
|
|
57
|
+
background: bg,
|
|
58
|
+
color: textColor,
|
|
59
|
+
paddingTop: typeof py === "number" ? `${py}px` : py,
|
|
60
|
+
paddingBottom: typeof py === "number" ? `${py}px` : py,
|
|
61
|
+
paddingLeft: typeof px === "number" ? `${px}px` : px,
|
|
62
|
+
paddingRight: typeof px === "number" ? `${px}px` : px,
|
|
63
|
+
textAlign: alignText,
|
|
64
|
+
height: h,
|
|
65
|
+
borderRadius: typeof radius === "number" ? `${radius}px` : radius,
|
|
66
|
+
boxSizing: "border-box",
|
|
67
|
+
overflow: "hidden",
|
|
68
|
+
...style,
|
|
69
|
+
};
|
|
70
|
+
}, [
|
|
71
|
+
background,
|
|
72
|
+
color,
|
|
73
|
+
overlay,
|
|
74
|
+
paddingY,
|
|
75
|
+
paddingX,
|
|
76
|
+
align,
|
|
77
|
+
height,
|
|
78
|
+
borderRadius,
|
|
79
|
+
theme,
|
|
80
|
+
screenSize,
|
|
81
|
+
style,
|
|
82
|
+
]);
|
|
83
|
+
const resolvedOverlay = resolveResponsive(overlay, screenSize);
|
|
84
|
+
return (_jsxs("section", { style: resolvedStyles, className: className, children: [resolvedOverlay && (_jsx("div", { style: {
|
|
85
|
+
position: "absolute",
|
|
86
|
+
inset: 0,
|
|
87
|
+
background: resolvedOverlay,
|
|
88
|
+
pointerEvents: "none",
|
|
89
|
+
} })), _jsxs(Container, { size: resolveResponsive(size, screenSize, "xl"), className: "relative z-10", children: [(title || subtitle) && (_jsxs("header", { style: { marginBottom: 32 }, children: [title && (_jsx("h2", { style: {
|
|
90
|
+
fontSize: "2rem",
|
|
91
|
+
fontWeight: 700,
|
|
92
|
+
marginBottom: subtitle ? 8 : 0,
|
|
93
|
+
}, children: title })), subtitle && (_jsx("p", { style: {
|
|
94
|
+
fontSize: "1.125rem",
|
|
95
|
+
color: resolvedVariant === "dark"
|
|
96
|
+
? "rgba(255,255,255,0.7)"
|
|
97
|
+
: "rgba(0,0,0,0.6)",
|
|
98
|
+
}, children: subtitle }))] })), children] })] }));
|
|
99
|
+
};
|
|
100
|
+
export default Section;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useMemo } from "react";
|
|
3
|
+
const getScreenSize = (width) => {
|
|
4
|
+
if (width < 768)
|
|
5
|
+
return "sm";
|
|
6
|
+
if (width < 1024)
|
|
7
|
+
return "md";
|
|
8
|
+
return "lg";
|
|
9
|
+
};
|
|
10
|
+
const resolveResponsive = (prop, screen, fallback) => {
|
|
11
|
+
if (prop == null)
|
|
12
|
+
return fallback;
|
|
13
|
+
if (typeof prop !== "object")
|
|
14
|
+
return prop;
|
|
15
|
+
return prop[screen] ?? fallback;
|
|
16
|
+
};
|
|
17
|
+
export const Stack = ({ direction = { sm: "vertical", md: "horizontal", lg: "horizontal" }, gap = 12, align = "center", justify = "flex-start", wrap = "nowrap", padding, margin, width = "100%", maxWidth, height = "auto", backgroundColor = "transparent", borderRadius, border, boxShadow, overflow, className, style, children, }) => {
|
|
18
|
+
const [screen, setScreen] = useState("lg");
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const updateScreen = () => setScreen(getScreenSize(window.innerWidth));
|
|
21
|
+
updateScreen();
|
|
22
|
+
window.addEventListener("resize", updateScreen);
|
|
23
|
+
return () => window.removeEventListener("resize", updateScreen);
|
|
24
|
+
}, []);
|
|
25
|
+
const computedStyle = useMemo(() => {
|
|
26
|
+
const toCssValue = (val) => typeof val === "number" ? `${val}px` : val;
|
|
27
|
+
const resolvedDir = resolveResponsive(direction, screen, "vertical");
|
|
28
|
+
const flexDir = resolvedDir === "vertical" ? "column" : "row";
|
|
29
|
+
return {
|
|
30
|
+
display: "flex",
|
|
31
|
+
flexDirection: flexDir,
|
|
32
|
+
alignItems: resolveResponsive(align, screen, "center"),
|
|
33
|
+
justifyContent: resolveResponsive(justify, screen, "flex-start"),
|
|
34
|
+
flexWrap: resolveResponsive(wrap, screen, "nowrap"),
|
|
35
|
+
gap: toCssValue(resolveResponsive(gap, screen, 12)),
|
|
36
|
+
padding: toCssValue(resolveResponsive(padding, screen, undefined)),
|
|
37
|
+
margin: toCssValue(resolveResponsive(margin, screen, undefined)),
|
|
38
|
+
width: resolveResponsive(width, screen, "100%"),
|
|
39
|
+
maxWidth: resolveResponsive(maxWidth, screen, undefined),
|
|
40
|
+
height: resolveResponsive(height, screen, undefined),
|
|
41
|
+
backgroundColor: resolveResponsive(backgroundColor, screen, undefined),
|
|
42
|
+
borderRadius: resolveResponsive(borderRadius, screen, undefined),
|
|
43
|
+
border: resolveResponsive(border, screen, undefined),
|
|
44
|
+
boxShadow: resolveResponsive(boxShadow, screen, undefined),
|
|
45
|
+
overflow: resolveResponsive(overflow, screen, undefined),
|
|
46
|
+
boxSizing: "border-box",
|
|
47
|
+
...style,
|
|
48
|
+
};
|
|
49
|
+
}, [
|
|
50
|
+
direction,
|
|
51
|
+
gap,
|
|
52
|
+
align,
|
|
53
|
+
justify,
|
|
54
|
+
wrap,
|
|
55
|
+
padding,
|
|
56
|
+
margin,
|
|
57
|
+
width,
|
|
58
|
+
maxWidth,
|
|
59
|
+
height,
|
|
60
|
+
backgroundColor,
|
|
61
|
+
borderRadius,
|
|
62
|
+
border,
|
|
63
|
+
boxShadow,
|
|
64
|
+
overflow,
|
|
65
|
+
style,
|
|
66
|
+
screen,
|
|
67
|
+
]);
|
|
68
|
+
return (_jsx("div", { className: className, style: computedStyle, children: children }));
|
|
69
|
+
};
|
|
70
|
+
export const HStack = (props) => {
|
|
71
|
+
return _jsx(Stack, { direction: "horizontal", ...props });
|
|
72
|
+
};
|
|
73
|
+
export const VStack = (props) => {
|
|
74
|
+
return _jsx(Stack, { direction: "vertical", ...props });
|
|
75
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
export const SwitchGroup = ({ name, options, selectedValues = [], onChange, disabled = false, readOnly = false, required = false, error, className = "", style, labelStyle, iconSize = 20, iconCheckedBgColor = "#2563eb", switchBgColor = "#d1d5db", 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" } }), _jsx("span", { style: {
|
|
24
|
+
position: "relative",
|
|
25
|
+
width: iconSize * 2,
|
|
26
|
+
height: iconSize * 1.1,
|
|
27
|
+
borderRadius: 9999,
|
|
28
|
+
backgroundColor: isChecked ? iconCheckedBgColor : switchBgColor,
|
|
29
|
+
transition: "background-color 0.25s ease",
|
|
30
|
+
}, children: _jsx("span", { style: {
|
|
31
|
+
position: "absolute",
|
|
32
|
+
top: "50%",
|
|
33
|
+
left: isChecked
|
|
34
|
+
? `calc(100% - ${iconSize - 4}px - 2px)`
|
|
35
|
+
: "2px",
|
|
36
|
+
transform: "translateY(-50%)",
|
|
37
|
+
width: iconSize - 4,
|
|
38
|
+
height: iconSize - 4,
|
|
39
|
+
borderRadius: "50%",
|
|
40
|
+
backgroundColor: "#fff",
|
|
41
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.3)",
|
|
42
|
+
transition: "left 0.25s ease",
|
|
43
|
+
} }) })] }, option.value));
|
|
44
|
+
}), error && (_jsx("p", { role: "alert", style: {
|
|
45
|
+
color: "#dc2626",
|
|
46
|
+
fontSize: 12,
|
|
47
|
+
marginTop: 4,
|
|
48
|
+
...errorStyle,
|
|
49
|
+
}, children: error }))] }));
|
|
50
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
export const Table = ({ children, style, className }) => (_jsx("table", { className: className, style: {
|
|
3
|
+
width: "100%",
|
|
4
|
+
borderCollapse: "collapse",
|
|
5
|
+
borderSpacing: 0,
|
|
6
|
+
...style,
|
|
7
|
+
}, children: children }));
|
|
8
|
+
export const THead = ({ children, style, className }) => (_jsx("thead", { className: className, style: {
|
|
9
|
+
backgroundColor: "#f9fafb",
|
|
10
|
+
borderBottom: "2px solid #e5e7eb",
|
|
11
|
+
...style,
|
|
12
|
+
}, children: children }));
|
|
13
|
+
export const TBody = ({ children, style, className }) => (_jsx("tbody", { className: className, style: style, children: children }));
|
|
14
|
+
export const TRow = ({ children, style, className }) => (_jsx("tr", { className: className, style: {
|
|
15
|
+
borderBottom: "1px solid #e5e7eb",
|
|
16
|
+
transition: "background 0.2s ease",
|
|
17
|
+
...style,
|
|
18
|
+
}, children: children }));
|
|
19
|
+
export const TH = ({ children, style, className }) => (_jsx("th", { className: className, style: {
|
|
20
|
+
textAlign: "left",
|
|
21
|
+
padding: "12px 16px",
|
|
22
|
+
fontWeight: 600,
|
|
23
|
+
fontSize: "0.875rem",
|
|
24
|
+
color: "#374151",
|
|
25
|
+
...style,
|
|
26
|
+
}, children: children }));
|
|
27
|
+
export const TD = ({ children, style, className }) => (_jsx("td", { className: className, style: {
|
|
28
|
+
padding: "12px 16px",
|
|
29
|
+
fontSize: "0.875rem",
|
|
30
|
+
color: "#4b5563",
|
|
31
|
+
...style,
|
|
32
|
+
}, children: children }));
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect, useRef } from "react";
|
|
4
|
+
/** 💎 Modern, Fully Customizable Tabs */
|
|
5
|
+
export const Tabs = ({ tabs, defaultActive = 0, position = "top", variant = "solid", fullWidth = false, gap = 8, radius = 8, padding = "12px 18px", transitionDuration = 200, elevation = 1, bordered = false, primaryColor = "#2563eb", backgroundColor = "transparent", textColor = "#374151", hoverColor = "#1d4ed8", activeColor = "#ffffff", borderColor = "#e5e7eb", disabledColor = "#9ca3af", responsiveBreakpoint = 768, showDrawerLabel = "Select Tab", drawerIcon = "☰", onTabChange, className = "", style, tabClassName = "", contentClassName = "", activeTabStyle, inactiveTabStyle, contentStyle, }) => {
|
|
6
|
+
const [active, setActive] = useState(defaultActive);
|
|
7
|
+
const [hovered, setHovered] = useState(null);
|
|
8
|
+
const [isMobile, setIsMobile] = useState(false);
|
|
9
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
10
|
+
const containerRef = useRef(null);
|
|
11
|
+
/** 📱 Responsive detection */
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const check = () => setIsMobile(window.innerWidth <= responsiveBreakpoint);
|
|
14
|
+
check();
|
|
15
|
+
window.addEventListener("resize", check);
|
|
16
|
+
return () => window.removeEventListener("resize", check);
|
|
17
|
+
}, [responsiveBreakpoint]);
|
|
18
|
+
/** 🎛 Handle tab change */
|
|
19
|
+
const handleChange = (i) => {
|
|
20
|
+
if (tabs[i].disabled)
|
|
21
|
+
return;
|
|
22
|
+
setActive(i);
|
|
23
|
+
onTabChange?.(i);
|
|
24
|
+
if (isMobile)
|
|
25
|
+
setDrawerOpen(false);
|
|
26
|
+
};
|
|
27
|
+
/** ⚡ Keyboard navigation */
|
|
28
|
+
const handleKeyDown = (e, i) => {
|
|
29
|
+
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
handleChange((i + 1) % tabs.length);
|
|
32
|
+
}
|
|
33
|
+
else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
handleChange((i - 1 + tabs.length) % tabs.length);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
/** 🎨 Base styles */
|
|
39
|
+
const baseTab = {
|
|
40
|
+
padding,
|
|
41
|
+
borderRadius: radius,
|
|
42
|
+
cursor: "pointer",
|
|
43
|
+
display: "flex",
|
|
44
|
+
alignItems: "center",
|
|
45
|
+
gap: 8,
|
|
46
|
+
justifyContent: "center",
|
|
47
|
+
fontWeight: 500,
|
|
48
|
+
transition: `all ${transitionDuration}ms ease`,
|
|
49
|
+
background: "transparent",
|
|
50
|
+
border: variant === "outline" ? `1px solid ${borderColor}` : "none",
|
|
51
|
+
borderBottom: variant === "underline" ? `2px solid transparent` : undefined,
|
|
52
|
+
color: textColor,
|
|
53
|
+
width: fullWidth ? "100%" : "auto",
|
|
54
|
+
userSelect: "none",
|
|
55
|
+
};
|
|
56
|
+
const activeTab = {
|
|
57
|
+
background: variant === "solid" ? primaryColor : "transparent",
|
|
58
|
+
color: activeColor,
|
|
59
|
+
borderBottom: variant === "underline" ? `2px solid ${primaryColor}` : undefined,
|
|
60
|
+
boxShadow: elevation > 0 ? `0 ${elevation}px ${elevation * 4}px ${primaryColor}33` : undefined,
|
|
61
|
+
...activeTabStyle,
|
|
62
|
+
};
|
|
63
|
+
const inactiveTab = {
|
|
64
|
+
...(variant === "outline"
|
|
65
|
+
? { borderColor }
|
|
66
|
+
: variant === "underline"
|
|
67
|
+
? { borderBottomColor: "transparent" }
|
|
68
|
+
: {}),
|
|
69
|
+
...inactiveTabStyle,
|
|
70
|
+
};
|
|
71
|
+
const hoverTab = {
|
|
72
|
+
color: hoverColor,
|
|
73
|
+
background: variant === "solid"
|
|
74
|
+
? `${primaryColor}11`
|
|
75
|
+
: variant === "outline"
|
|
76
|
+
? `${primaryColor}11`
|
|
77
|
+
: "transparent",
|
|
78
|
+
};
|
|
79
|
+
const disabledTab = {
|
|
80
|
+
color: disabledColor,
|
|
81
|
+
cursor: "not-allowed",
|
|
82
|
+
opacity: 0.6,
|
|
83
|
+
};
|
|
84
|
+
const contentBox = {
|
|
85
|
+
flexGrow: 1,
|
|
86
|
+
borderTop: bordered && position === "top" ? `1px solid ${borderColor}` : undefined,
|
|
87
|
+
borderLeft: bordered && position === "left" ? `1px solid ${borderColor}` : undefined,
|
|
88
|
+
borderRight: bordered && position === "right" ? `1px solid ${borderColor}` : undefined,
|
|
89
|
+
borderRadius: radius,
|
|
90
|
+
...contentStyle,
|
|
91
|
+
};
|
|
92
|
+
/** 🧱 Layout */
|
|
93
|
+
const isVertical = position === "left" || position === "right";
|
|
94
|
+
const layoutDir = isVertical ? "row" : "column";
|
|
95
|
+
return (_jsxs("div", { ref: containerRef, className: `modern-tabs ${className}`, style: {
|
|
96
|
+
display: "flex",
|
|
97
|
+
flexDirection: isVertical ? (position === "right" ? "row-reverse" : "row") : "column",
|
|
98
|
+
background: backgroundColor,
|
|
99
|
+
border: bordered ? `1px solid ${borderColor}` : undefined,
|
|
100
|
+
borderRadius: radius,
|
|
101
|
+
overflow: "hidden",
|
|
102
|
+
...style,
|
|
103
|
+
}, children: [_jsx("style", { children: `
|
|
104
|
+
@keyframes tab-fade {
|
|
105
|
+
from { opacity: 0; transform: translateY(5px); }
|
|
106
|
+
to { opacity: 1; transform: translateY(0); }
|
|
107
|
+
}
|
|
108
|
+
.modern-tabs__content {
|
|
109
|
+
animation: tab-fade ${transitionDuration}ms ease;
|
|
110
|
+
}
|
|
111
|
+
` }), isMobile ? (_jsxs("div", { style: { width: "100%", padding: 8 }, children: [_jsxs("button", { onClick: () => setDrawerOpen(!drawerOpen), style: {
|
|
112
|
+
...baseTab,
|
|
113
|
+
...activeTab,
|
|
114
|
+
justifyContent: "space-between",
|
|
115
|
+
width: "100%",
|
|
116
|
+
fontSize: 16,
|
|
117
|
+
}, children: [showDrawerLabel, _jsx("span", { children: drawerIcon })] }), drawerOpen && (_jsx("div", { style: {
|
|
118
|
+
display: "flex",
|
|
119
|
+
flexDirection: "column",
|
|
120
|
+
marginTop: 8,
|
|
121
|
+
gap,
|
|
122
|
+
}, children: tabs.map((tab, i) => {
|
|
123
|
+
const isActive = i === active;
|
|
124
|
+
const isHovered = hovered === i;
|
|
125
|
+
const isDisabled = tab.disabled;
|
|
126
|
+
return (_jsxs("button", { disabled: isDisabled, onClick: () => handleChange(i), onKeyDown: (e) => handleKeyDown(e, i), onMouseEnter: () => setHovered(i), onMouseLeave: () => setHovered(null), className: tabClassName, style: {
|
|
127
|
+
...baseTab,
|
|
128
|
+
...(isActive ? activeTab : inactiveTab),
|
|
129
|
+
...(isHovered && !isActive && !isDisabled ? hoverTab : {}),
|
|
130
|
+
...(isDisabled ? disabledTab : {}),
|
|
131
|
+
}, children: [tab.icon && _jsx("span", { children: tab.icon }), tab.label] }, i));
|
|
132
|
+
}) }))] })) : (_jsx("div", { style: {
|
|
133
|
+
display: "flex",
|
|
134
|
+
flexDirection: isVertical ? "column" : "row",
|
|
135
|
+
gap,
|
|
136
|
+
padding: 8,
|
|
137
|
+
minWidth: isVertical ? 200 : undefined,
|
|
138
|
+
}, children: tabs.map((tab, i) => {
|
|
139
|
+
const isActive = i === active;
|
|
140
|
+
const isHovered = hovered === i;
|
|
141
|
+
const isDisabled = tab.disabled;
|
|
142
|
+
return (_jsxs("button", { disabled: isDisabled, onClick: () => handleChange(i), onKeyDown: (e) => handleKeyDown(e, i), onMouseEnter: () => setHovered(i), onMouseLeave: () => setHovered(null), className: tabClassName, style: {
|
|
143
|
+
...baseTab,
|
|
144
|
+
...(isActive ? activeTab : inactiveTab),
|
|
145
|
+
...(isHovered && !isActive && !isDisabled ? hoverTab : {}),
|
|
146
|
+
...(isDisabled ? disabledTab : {}),
|
|
147
|
+
}, role: "tab", "aria-selected": isActive, children: [tab.icon && _jsx("span", { children: tab.icon }), tab.label] }, i));
|
|
148
|
+
}) })), _jsx("div", { className: `modern-tabs__content ${contentClassName}`, style: contentBox, role: "tabpanel", children: tabs[active]?.content })] }));
|
|
149
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
/** 🎨 Utility: generate lighter/darker shades */
|
|
3
|
+
function adjustColor(color, amount) {
|
|
4
|
+
return ("#" +
|
|
5
|
+
color
|
|
6
|
+
.replace(/^#/, "")
|
|
7
|
+
.replace(/../g, (hex) => ("0" +
|
|
8
|
+
Math.min(255, Math.max(0, parseInt(hex, 16) + amount)).toString(16)).slice(-2)));
|
|
9
|
+
}
|
|
10
|
+
/** 🌈 Default color palettes */
|
|
11
|
+
const defaultColors = {
|
|
12
|
+
light: {
|
|
13
|
+
default: "#111",
|
|
14
|
+
primary: "#2563eb",
|
|
15
|
+
secondary: "#64748b",
|
|
16
|
+
success: "#16a34a",
|
|
17
|
+
danger: "#dc2626",
|
|
18
|
+
white: "#ffffff",
|
|
19
|
+
muted: "#6b7280",
|
|
20
|
+
},
|
|
21
|
+
dark: {
|
|
22
|
+
default: "#f8fafc",
|
|
23
|
+
primary: "#60a5fa",
|
|
24
|
+
secondary: "#94a3b8",
|
|
25
|
+
success: "#22c55e",
|
|
26
|
+
danger: "#f87171",
|
|
27
|
+
white: "#ffffff",
|
|
28
|
+
muted: "#9ca3af",
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
/** 💬 Main Component (HTML-only tags) */
|
|
32
|
+
export function Text({ as, children, color = "default", size = "md", weight = "normal", align = "left", transform = "none", italic = false, underline = false, strikethrough = false, truncate = false, selectable = true, hoverable = false, onClick, darkMode = false, baseColor, className = "", style = {}, ...rest }) {
|
|
33
|
+
// element type as provided (constrained to HTML tags)
|
|
34
|
+
const Element = (as || "p");
|
|
35
|
+
/** 🎨 Dynamic color palette generation */
|
|
36
|
+
const theme = useMemo(() => {
|
|
37
|
+
if (!baseColor)
|
|
38
|
+
return defaultColors[darkMode ? "dark" : "light"];
|
|
39
|
+
const lightMode = {
|
|
40
|
+
default: "#111",
|
|
41
|
+
primary: baseColor,
|
|
42
|
+
secondary: adjustColor(baseColor, -50),
|
|
43
|
+
success: adjustColor(baseColor, -30),
|
|
44
|
+
danger: "#dc2626",
|
|
45
|
+
white: "#ffffff",
|
|
46
|
+
muted: "#6b7280",
|
|
47
|
+
};
|
|
48
|
+
const darkModeTheme = {
|
|
49
|
+
default: "#f8fafc",
|
|
50
|
+
primary: adjustColor(baseColor, 80),
|
|
51
|
+
secondary: adjustColor(baseColor, 120),
|
|
52
|
+
success: adjustColor(baseColor, 100),
|
|
53
|
+
danger: "#f87171",
|
|
54
|
+
white: "#ffffff",
|
|
55
|
+
muted: "#9ca3af",
|
|
56
|
+
};
|
|
57
|
+
return darkMode ? darkModeTheme : lightMode;
|
|
58
|
+
}, [baseColor, darkMode]);
|
|
59
|
+
/** 📏 Font sizes */
|
|
60
|
+
const sizes = {
|
|
61
|
+
xs: "0.75rem",
|
|
62
|
+
sm: "0.875rem",
|
|
63
|
+
md: "1rem",
|
|
64
|
+
lg: "1.25rem",
|
|
65
|
+
xl: "1.5rem",
|
|
66
|
+
"2xl": "2rem",
|
|
67
|
+
};
|
|
68
|
+
/** 💅 Computed styles */
|
|
69
|
+
const computedStyle = {
|
|
70
|
+
color: theme[color] || color,
|
|
71
|
+
fontSize: sizes[size] || size,
|
|
72
|
+
fontWeight: weight,
|
|
73
|
+
textAlign: align,
|
|
74
|
+
textTransform: transform,
|
|
75
|
+
fontStyle: italic ? "italic" : "normal",
|
|
76
|
+
textDecoration: underline ? "underline" : strikethrough ? "line-through" : "none",
|
|
77
|
+
userSelect: selectable ? "text" : "none",
|
|
78
|
+
overflow: truncate ? "hidden" : undefined,
|
|
79
|
+
whiteSpace: truncate ? "nowrap" : undefined,
|
|
80
|
+
textOverflow: truncate ? "ellipsis" : undefined,
|
|
81
|
+
cursor: onClick ? "pointer" : "default",
|
|
82
|
+
transition: "all 0.25s ease-in-out",
|
|
83
|
+
...style,
|
|
84
|
+
};
|
|
85
|
+
/** 🧠 Hover events (narrow to HTMLElement at runtime) */
|
|
86
|
+
const handleMouseEnter = (e) => {
|
|
87
|
+
if (!hoverable)
|
|
88
|
+
return;
|
|
89
|
+
const target = e.currentTarget;
|
|
90
|
+
if (target instanceof HTMLElement) {
|
|
91
|
+
target.style.opacity = "0.8";
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const handleMouseLeave = (e) => {
|
|
95
|
+
if (!hoverable)
|
|
96
|
+
return;
|
|
97
|
+
const target = e.currentTarget;
|
|
98
|
+
if (target instanceof HTMLElement) {
|
|
99
|
+
target.style.opacity = "1";
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Build props object. We cast only once to the correct intrinsic props type.
|
|
104
|
+
* This prevents TypeScript from having to compute giant unions for event types.
|
|
105
|
+
*/
|
|
106
|
+
const props = {
|
|
107
|
+
className,
|
|
108
|
+
style: computedStyle,
|
|
109
|
+
onClick,
|
|
110
|
+
onMouseEnter: handleMouseEnter,
|
|
111
|
+
onMouseLeave: handleMouseLeave,
|
|
112
|
+
...rest,
|
|
113
|
+
};
|
|
114
|
+
// Use React.createElement to avoid JSX generic complexity
|
|
115
|
+
return React.createElement(Element, props, children);
|
|
116
|
+
}
|
|
117
|
+
export default Text;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import "./style.css"; // 🔹 Global styles import (keep this as the only CSS import)
|
|
2
|
+
// ==============================
|
|
3
|
+
// ✅ BASIC COMPONENT EXPORTS
|
|
4
|
+
// ==============================
|
|
5
|
+
// Alerts & Notifications
|
|
6
|
+
export { Alert } from "./components/basic/Alert"; // ✅ Finalized
|
|
7
|
+
// Accordions
|
|
8
|
+
export { Accordion } from "./components/basic/Accordation"; // ✅ Finalized
|
|
9
|
+
// Avatars
|
|
10
|
+
export { Avatar, AvatarGroup } from "./components/basic/Avatar"; // ✅ Finalized
|
|
11
|
+
// Audio Components
|
|
12
|
+
export { AudioPlayer } from "./components/basic/AudioPlayer"; // ✅ Finalized
|
|
13
|
+
export { AudioGallery } from "./components/basic/AudioGallery"; // ✅ Finalized
|
|
14
|
+
// Badges
|
|
15
|
+
export { Badge } from "./components/basic/Badge"; // ✅ Finalized
|
|
16
|
+
// Buttons
|
|
17
|
+
export { Button } from "./components/basic/Button"; // ✅ Finalized
|
|
18
|
+
// Cards
|
|
19
|
+
export { Card } from "./components/basic/Card"; // ✅ Finalized
|
|
20
|
+
// ✅ Checkbox, Radio & Switch Components
|
|
21
|
+
export { CheckboxGroup } from "./components/basic/CheckboxGroup";
|
|
22
|
+
export { RadioGroup } from "./components/basic/RadioGroup";
|
|
23
|
+
export { SwitchGroup } from "./components/basic/SwitchGroup";
|
|
24
|
+
// Containers & Layout
|
|
25
|
+
export { Container } from "./components/basic/Container"; // ✅ Finalized
|
|
26
|
+
export { Flexbox } from "./components/basic/Flexbox"; // ✅ Finalized
|
|
27
|
+
export { Stack, HStack, VStack } from "./components/basic/Stack"; // ✅ Finalized
|
|
28
|
+
export { GridView } from "./components/basic/GridView"; // ✅ Finalized
|
|
29
|
+
// Drawers
|
|
30
|
+
export { Drawer, DrawerButton } from "./components/basic/Drawer"; // ✅ Finalized
|
|
31
|
+
// Dropdowns
|
|
32
|
+
export { Dropdown } from "./components/basic/DropDown"; // ✅ Finalized
|
|
33
|
+
// Forms & Inputs
|
|
34
|
+
export { Input } from "./components/basic/Input"; // ✅ Finalized
|
|
35
|
+
// Images
|
|
36
|
+
export { Image } from "./components/basic/Image"; // ✅ Finalized
|
|
37
|
+
// Lists
|
|
38
|
+
export { List } from "./components/basic/List"; // ✅ Finalized
|
|
39
|
+
// Modals
|
|
40
|
+
export { Modal } from "./components/basic/Modal"; // ✅ Finalized
|
|
41
|
+
// Tables
|
|
42
|
+
export { Table, TBody, TD, THead, TH, TRow } from "./components/basic/Table"; // ✅ Finalized
|
|
43
|
+
// Tabs
|
|
44
|
+
export { Tabs } from "./components/basic/Tabs"; // ✅ Finalized
|
|
45
|
+
// Text
|
|
46
|
+
export { Text } from "./components/basic/Text"; // ✅ Finalized
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface AccordionItem {
|
|
3
|
+
title: string;
|
|
4
|
+
content: React.ReactNode;
|
|
5
|
+
}
|
|
6
|
+
export interface AccordionProps {
|
|
7
|
+
items: AccordionItem[];
|
|
8
|
+
/** Allow multiple open items */
|
|
9
|
+
allowMultiple?: boolean;
|
|
10
|
+
/** Default open indexes */
|
|
11
|
+
defaultOpen?: number[];
|
|
12
|
+
/** Appearance */
|
|
13
|
+
borderColor?: string;
|
|
14
|
+
backgroundColor?: string;
|
|
15
|
+
textColor?: string;
|
|
16
|
+
hoverBgColor?: string;
|
|
17
|
+
hoverTextColor?: string;
|
|
18
|
+
contentBgColor?: string;
|
|
19
|
+
contentTextColor?: string;
|
|
20
|
+
/** Layout & spacing */
|
|
21
|
+
paddingY?: string | number;
|
|
22
|
+
paddingX?: string | number;
|
|
23
|
+
marginY?: string | number;
|
|
24
|
+
borderRadius?: string | number;
|
|
25
|
+
contentPadding?: string | number;
|
|
26
|
+
/** Typography */
|
|
27
|
+
fontSize?: string | number;
|
|
28
|
+
fontWeight?: string | number;
|
|
29
|
+
contentFontSize?: string | number;
|
|
30
|
+
contentFontWeight?: string | number;
|
|
31
|
+
/** Icon customization */
|
|
32
|
+
iconOpen?: React.ReactNode;
|
|
33
|
+
iconClose?: React.ReactNode;
|
|
34
|
+
iconSize?: string | number;
|
|
35
|
+
/** Motion & style */
|
|
36
|
+
transitionDuration?: string;
|
|
37
|
+
shadow?: string;
|
|
38
|
+
className?: string;
|
|
39
|
+
style?: React.CSSProperties;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 🧠 Industry-standard, minimal, and fully customizable Accordion
|
|
43
|
+
*/
|
|
44
|
+
export declare const Accordion: React.FC<AccordionProps>;
|