@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,123 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useRef, useImperativeHandle, forwardRef, } from "react";
4
+ import { Eye, EyeOff } from "lucide-react";
5
+ export const Input = forwardRef((props, ref) => {
6
+ const { type = "text", label, placeholder = "", name = "", value, defaultValue, onChange, disabled = false, readOnly = false, required = false, error, success = false, autoFocus = false, iconLeft, iconRight,
7
+ /** 🎨 Styling props */
8
+ labelColor = "#374151", placeholderColor = "#9ca3af", backgroundColor = "#ffffff", textColor = "#111827", borderColor = "#d1d5db", hoverBorderColor = "#9ca3af", focusBorderColor = "#2563eb", errorColor = "#dc2626", successColor = "#16a34a", iconColor = "#6b7280", shadow = "0 1px 2px rgba(0,0,0,0.05)",
9
+ /** 📏 Layout + size */
10
+ size = "md", fontSize = "14px", fontFamily = "Inter, system-ui, sans-serif", radius = "8px", rows = 4, cols, maxLength, resize = true, showCharacterCount = true, paddingX, paddingY, className, style, } = props;
11
+ const inputRef = useRef(null);
12
+ const [localValue, setLocalValue] = useState(defaultValue || "");
13
+ const [visible, setVisible] = useState(false);
14
+ useImperativeHandle(ref, () => inputRef.current);
15
+ /** ✅ Make it controlled properly */
16
+ const handleChange = (e) => {
17
+ const newValue = e.target.value;
18
+ if (maxLength && newValue.length > maxLength)
19
+ return;
20
+ setLocalValue(newValue);
21
+ if (onChange)
22
+ onChange(name, newValue);
23
+ };
24
+ const currentValue = value !== undefined ? value : localValue;
25
+ /** 🎯 Dynamic border colors */
26
+ const currentBorderColor = error
27
+ ? errorColor
28
+ : success
29
+ ? successColor
30
+ : borderColor;
31
+ /** 🧠 Size tokens */
32
+ const sizes = {
33
+ sm: { paddingY: "6px", paddingX: "10px", font: "13px" },
34
+ md: { paddingY: "10px", paddingX: "14px", font: "14px" },
35
+ lg: { paddingY: "14px", paddingX: "18px", font: "16px" },
36
+ }[size];
37
+ const px = paddingX || sizes.paddingX;
38
+ const py = paddingY || sizes.paddingY;
39
+ /** 💅 Base input styles */
40
+ const baseInputStyle = {
41
+ width: "100%",
42
+ border: `1px solid ${currentBorderColor}`,
43
+ borderRadius: radius,
44
+ backgroundColor,
45
+ color: textColor,
46
+ fontFamily,
47
+ fontSize,
48
+ padding: `${py} ${px}`,
49
+ paddingLeft: iconLeft ? "40px" : px,
50
+ paddingRight: iconRight || type === "password" ? "40px" : px,
51
+ outline: "none",
52
+ transition: "border-color 0.25s ease, box-shadow 0.25s ease",
53
+ resize: type === "textarea" && !resize ? "none" : undefined,
54
+ boxShadow: shadow,
55
+ ...style,
56
+ };
57
+ /** 🧠 Placeholder dynamic color */
58
+ const dynamicPlaceholder = {
59
+ "::placeholder": {
60
+ color: placeholderColor,
61
+ opacity: 1,
62
+ },
63
+ };
64
+ /** 🎨 Dynamic border behavior */
65
+ const applyDynamicBorder = (el, color) => {
66
+ if (el)
67
+ el.style.borderColor = color;
68
+ };
69
+ const commonEvents = {
70
+ onFocus: (e) => applyDynamicBorder(e.currentTarget, focusBorderColor),
71
+ onBlur: (e) => applyDynamicBorder(e.currentTarget, currentBorderColor),
72
+ onMouseEnter: (e) => applyDynamicBorder(e.currentTarget, hoverBorderColor),
73
+ onMouseLeave: (e) => applyDynamicBorder(e.currentTarget, currentBorderColor),
74
+ };
75
+ return (_jsxs("div", { className: className, style: {
76
+ width: "100%",
77
+ display: "flex",
78
+ flexDirection: "column",
79
+ fontFamily,
80
+ }, children: [label && (_jsxs("label", { htmlFor: name, style: {
81
+ marginBottom: 6,
82
+ color: labelColor,
83
+ fontWeight: 500,
84
+ fontSize: "14px",
85
+ }, children: [label, required && _jsx("span", { style: { color: errorColor }, children: " *" })] })), _jsxs("div", { style: { position: "relative", width: "100%" }, children: [iconLeft && (_jsx("span", { style: {
86
+ position: "absolute",
87
+ left: 12,
88
+ top: "50%",
89
+ transform: "translateY(-50%)",
90
+ color: iconColor,
91
+ pointerEvents: "none",
92
+ }, children: iconLeft })), type === "textarea" ? (_jsx("textarea", { ref: inputRef, name: name, value: currentValue, placeholder: placeholder, disabled: disabled, readOnly: readOnly, rows: rows, cols: cols, maxLength: maxLength, autoFocus: autoFocus, style: { ...baseInputStyle, ...dynamicPlaceholder }, onChange: handleChange, ...commonEvents })) : (_jsx("input", { ref: inputRef, id: name, type: type === "password"
93
+ ? visible
94
+ ? "text"
95
+ : "password"
96
+ : type, name: name, value: currentValue, placeholder: placeholder, disabled: disabled, readOnly: readOnly, autoFocus: autoFocus, style: { ...baseInputStyle, ...dynamicPlaceholder }, onChange: handleChange, ...commonEvents })), type === "password" && (_jsx("button", { type: "button", onClick: () => setVisible(!visible), style: {
97
+ position: "absolute",
98
+ right: 10,
99
+ top: "50%",
100
+ transform: "translateY(-50%)",
101
+ background: "transparent",
102
+ border: "none",
103
+ cursor: "pointer",
104
+ color: iconColor,
105
+ padding: 0,
106
+ }, children: visible ? _jsx(EyeOff, { size: 18 }) : _jsx(Eye, { size: 18 }) })), iconRight && type !== "password" && (_jsx("span", { style: {
107
+ position: "absolute",
108
+ right: 12,
109
+ top: "50%",
110
+ transform: "translateY(-50%)",
111
+ color: iconColor,
112
+ pointerEvents: "none",
113
+ }, children: iconRight }))] }), type === "textarea" && showCharacterCount && maxLength && (_jsxs("div", { style: {
114
+ textAlign: "right",
115
+ fontSize: "12px",
116
+ color: "#6b7280",
117
+ marginTop: 4,
118
+ }, children: [currentValue.length, "/", maxLength] })), error && (_jsx("div", { style: {
119
+ color: errorColor,
120
+ fontSize: "12px",
121
+ marginTop: 4,
122
+ }, children: error }))] }));
123
+ });
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export const ListItem = ({ text, icon, onClick, subItems, bulletColor = "#2563eb", textColor = "#111827", fontSize = "15px", fontWeight = 500, spacing = "12px", isInline = false, }) => {
3
+ const itemContainerStyle = {
4
+ display: "flex",
5
+ flexDirection: "column",
6
+ gap: "6px",
7
+ marginBottom: isInline ? "0" : spacing,
8
+ };
9
+ const contentStyle = {
10
+ display: "flex",
11
+ alignItems: "center",
12
+ gap: "8px",
13
+ fontSize,
14
+ fontWeight,
15
+ color: textColor,
16
+ cursor: onClick ? "pointer" : "default",
17
+ transition: "color 0.2s ease, transform 0.2s ease",
18
+ };
19
+ const bulletStyle = {
20
+ width: "8px",
21
+ height: "8px",
22
+ backgroundColor: bulletColor,
23
+ borderRadius: "50%",
24
+ flexShrink: 0,
25
+ };
26
+ const subListStyle = {
27
+ listStyleType: "disc",
28
+ paddingLeft: "20px",
29
+ margin: 0,
30
+ };
31
+ return (_jsxs("li", { style: itemContainerStyle, children: [_jsxs("div", { style: contentStyle, onClick: onClick, onMouseEnter: (e) => (e.currentTarget.style.color = bulletColor), onMouseLeave: (e) => (e.currentTarget.style.color = textColor), children: [icon ? (_jsx("span", { style: { fontSize: "16px", color: textColor }, children: icon })) : (!isInline && _jsx("span", { style: bulletStyle })), _jsx("span", { children: text })] }), subItems && subItems.length > 0 && (_jsx("ul", { style: subListStyle, children: subItems.map((sub, index) => (_jsx(ListItem, { ...sub, bulletColor: bulletColor, textColor: textColor, fontSize: fontSize, fontWeight: fontWeight, spacing: spacing, isInline: false }, index))) }))] }));
32
+ };
33
+ /* -------------------------------------------------------------------------- */
34
+ /* 📋 List */
35
+ /* -------------------------------------------------------------------------- */
36
+ export const List = ({ title, titleIcon, items, type = "unordered", bulletColor = "#2563eb", textColor = "#111827", backgroundColor = "#fff", borderColor = "#e5e7eb", fontSize = "15px", fontWeight = 500, borderRadius = "12px", padding = "16px", spacing = "12px", className, style, }) => {
37
+ const isOrdered = type === "ordered";
38
+ const isInline = type === "inline";
39
+ const containerStyle = {
40
+ backgroundColor,
41
+ borderColor,
42
+ color: textColor,
43
+ borderWidth: borderColor ? "1px" : "0px",
44
+ borderStyle: "solid",
45
+ borderRadius,
46
+ padding,
47
+ ...style,
48
+ };
49
+ const listStyle = isInline
50
+ ? {
51
+ display: "flex",
52
+ gap: spacing,
53
+ paddingLeft: 0,
54
+ listStyleType: "none",
55
+ margin: 0,
56
+ }
57
+ : {
58
+ listStyleType: isOrdered ? "decimal" : "none",
59
+ paddingLeft: isOrdered ? "20px" : "0",
60
+ margin: 0,
61
+ };
62
+ const ListTag = isOrdered ? "ol" : "ul";
63
+ return (_jsxs("div", { className: className, style: containerStyle, children: [title && (_jsxs("div", { style: {
64
+ display: "flex",
65
+ alignItems: "center",
66
+ fontSize: "17px",
67
+ fontWeight: 600,
68
+ marginBottom: "10px",
69
+ gap: "8px",
70
+ }, children: [titleIcon && _jsx("span", { style: { fontSize: "18px" }, children: titleIcon }), _jsx("span", { children: title })] })), _jsx(ListTag, { style: listStyle, children: items.map((item, index) => (_jsx(ListItem, { ...item, bulletColor: bulletColor, textColor: textColor, fontSize: fontSize, fontWeight: fontWeight, spacing: spacing, isInline: isInline }, index))) })] }));
71
+ };
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useCallback, } from "react";
3
+ import { X } from "lucide-react";
4
+ /* -------------------------------------------------------------------------- */
5
+ /* Modal */
6
+ /* -------------------------------------------------------------------------- */
7
+ export const Modal = ({ isOpen, onClose, children, ariaLabel, title, overlayStyle, modalStyle, closeButtonStyle, disableOverlayClose = false, transitionDuration = 200, className, }) => {
8
+ const modalRef = useRef(null);
9
+ /* ------------------------------ Escape Close ----------------------------- */
10
+ useEffect(() => {
11
+ const handleEsc = (e) => {
12
+ if (e.key === "Escape")
13
+ onClose();
14
+ };
15
+ if (isOpen)
16
+ document.addEventListener("keydown", handleEsc);
17
+ return () => document.removeEventListener("keydown", handleEsc);
18
+ }, [isOpen, onClose]);
19
+ /* ------------------------------ Scroll Lock ------------------------------ */
20
+ useEffect(() => {
21
+ if (isOpen) {
22
+ const prev = document.body.style.overflow;
23
+ document.body.style.overflow = "hidden";
24
+ return () => {
25
+ document.body.style.overflow = prev;
26
+ };
27
+ }
28
+ }, [isOpen]);
29
+ /* ------------------------------ Click Outside ---------------------------- */
30
+ const handleOverlayClick = useCallback(() => {
31
+ if (!disableOverlayClose)
32
+ onClose();
33
+ }, [disableOverlayClose, onClose]);
34
+ /* ------------------------------ Early Return ----------------------------- */
35
+ if (!isOpen)
36
+ return null;
37
+ /* ------------------------------- Base Styles ----------------------------- */
38
+ const baseOverlay = {
39
+ position: "fixed",
40
+ inset: 0,
41
+ backgroundColor: "rgba(0,0,0,0.6)",
42
+ display: "flex",
43
+ justifyContent: "center",
44
+ alignItems: "center",
45
+ zIndex: 1000,
46
+ opacity: isOpen ? 1 : 0,
47
+ transition: `opacity ${transitionDuration}ms ease`,
48
+ ...overlayStyle,
49
+ };
50
+ const baseModal = {
51
+ position: "relative",
52
+ backgroundColor: "#fff",
53
+ borderRadius: 12,
54
+ width: "90vw",
55
+ maxWidth: 700,
56
+ maxHeight: "90vh",
57
+ overflowY: "auto",
58
+ padding: 24,
59
+ boxShadow: "0 10px 40px rgba(0,0,0,0.25)",
60
+ transform: isOpen ? "scale(1)" : "scale(0.95)",
61
+ transition: `transform ${transitionDuration}ms ease, opacity ${transitionDuration}ms ease`,
62
+ ...modalStyle,
63
+ };
64
+ const baseCloseBtn = {
65
+ position: "absolute",
66
+ top: 16,
67
+ right: 16,
68
+ background: "transparent",
69
+ border: "none",
70
+ cursor: "pointer",
71
+ padding: 4,
72
+ color: "#444",
73
+ transition: "color 0.2s ease, transform 0.2s ease",
74
+ ...closeButtonStyle,
75
+ };
76
+ /* ----------------------------- Render Content ---------------------------- */
77
+ return (_jsx("div", { role: "dialog", "aria-modal": "true", "aria-label": ariaLabel || title || "Modal", style: baseOverlay, onClick: handleOverlayClick, className: className, children: _jsxs("div", { ref: modalRef, style: baseModal, onClick: (e) => e.stopPropagation(), children: [_jsx("button", { onClick: onClose, "aria-label": "Close modal", style: baseCloseBtn, onMouseEnter: (e) => {
78
+ e.currentTarget.style.color = "#000";
79
+ e.currentTarget.style.transform = "scale(1.1)";
80
+ }, onMouseLeave: (e) => {
81
+ e.currentTarget.style.color = "#444";
82
+ e.currentTarget.style.transform = "scale(1)";
83
+ }, children: _jsx(X, { size: 24 }) }), title && (_jsx("h2", { style: {
84
+ fontSize: "1.25rem",
85
+ fontWeight: 600,
86
+ marginBottom: "1rem",
87
+ }, children: title })), children] }) }));
88
+ };
@@ -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,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
+ };