@neuctra/ui 0.2.6 → 0.2.8

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.
@@ -5,24 +5,21 @@ import clsx from "clsx";
5
5
  /* -------------------------------------------------------------------------- */
6
6
  /* 🔘 Button */
7
7
  /* -------------------------------------------------------------------------- */
8
- export const Button = forwardRef(({ children, iconBefore, iconAfter, loading = false, loadingText = "Loading...", fullWidth = false, primaryTheme = true, primaryColor = "#3b82f6", size = "md", disabled, className, ...props }, ref) => {
8
+ export const Button = forwardRef(({ children, iconBefore, iconAfter, loading = false, loadingText = "Loading...", fullWidth = false, variant = "default", size = "md", disabled, className, type = "button", // important fix
9
+ ...props }, ref) => {
10
+ const isDisabled = disabled || loading;
9
11
  /** 📏 Sizes */
10
12
  const sizeClasses = {
11
- sm: "px-3 py-1.5 text-sm",
12
- md: "px-5 py-2.5 text-sm",
13
- lg: "px-6 py-3 text-base",
13
+ sm: "h-8 px-3 text-sm",
14
+ md: "h-10 px-4 text-sm",
15
+ lg: "h-12 px-6 text-base",
14
16
  };
15
- /** 🎨 Theme styles */
16
- const themeClasses = primaryTheme
17
- ? "bg-[var(--primary)] text-white hover:bg-[var(--primary)]/90 focus:ring-[var(--primary)]/30"
18
- : "";
19
- /** 🎨 Dynamic fallback for non-primary theme */
20
- const dynamicStyle = !primaryTheme
21
- ? {
22
- backgroundColor: primaryColor,
23
- color: "#fff",
24
- }
25
- : {};
26
- return (_jsx("button", { ref: ref, disabled: disabled || loading, style: !primaryTheme ? dynamicStyle : undefined, className: clsx("inline-flex items-center justify-center gap-2 font-medium rounded-lg transition-all duration-200", "focus:outline-none focus:ring-2 focus:ring-offset-1", sizeClasses[size], themeClasses, fullWidth && "w-full", (disabled || loading) && "opacity-60 cursor-not-allowed", className), ...props, children: loading ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "w-4 h-4 border-2 border-current border-t-transparent rounded-full animate-spin" }), _jsx("span", { children: loadingText })] })) : (_jsxs(_Fragment, { children: [iconBefore && (_jsx("span", { className: "flex items-center justify-center", children: iconBefore })), _jsx("span", { children: children }), iconAfter && (_jsx("span", { className: "flex items-center justify-center", children: iconAfter }))] })) }));
17
+ /** 🎨 Variants */
18
+ const variantClasses = {
19
+ default: "bg-blue-600 text-white hover:bg-blue-600/90",
20
+ outline: "border border-zinc-300 text-zinc-900 hover:bg-zinc-100 dark:border-zinc-700 dark:text-white dark:hover:bg-zinc-800",
21
+ ghost: "text-zinc-900 hover:bg-zinc-100 dark:text-white dark:hover:bg-zinc-800",
22
+ };
23
+ return (_jsx("button", { ref: ref, type: type, disabled: isDisabled, className: clsx("inline-flex items-center justify-center gap-2 rounded-lg font-medium transition-all duration-200", "focus:outline-none focus:ring-2 focus:ring-blue-500/30", "active:scale-[0.98]", sizeClasses[size], variantClasses[variant], fullWidth && "w-full", isDisabled && "opacity-60 cursor-not-allowed pointer-events-none", className), ...props, children: loading ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "w-4 h-4 border-2 border-current border-t-transparent rounded-full animate-spin" }), _jsx("span", { children: loadingText })] })) : (_jsxs(_Fragment, { children: [iconBefore && _jsx("span", { className: "flex", children: iconBefore }), _jsx("span", { children: children }), iconAfter && _jsx("span", { className: "flex", children: iconAfter })] })) }));
27
24
  });
28
25
  Button.displayName = "Button";
@@ -1,117 +1,25 @@
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
- },
1
+ "use client";
2
+ import React from "react";
3
+ import clsx from "clsx";
4
+ const sizeClasses = {
5
+ xs: "text-xs",
6
+ sm: "text-sm",
7
+ md: "text-base",
8
+ lg: "text-lg",
9
+ xl: "text-xl",
10
+ "2xl": "text-2xl",
30
11
  };
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)
12
+ export function Text({ as, children, size = "md", weight = 400, align = "left", truncate = false, className, style, ...rest }) {
34
13
  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,
14
+ return React.createElement(Element, {
15
+ className: clsx("text-inherit", // inherits parent color
16
+ sizeClasses[size] || "", truncate && "truncate", className),
17
+ style: {
18
+ fontWeight: weight,
19
+ textAlign: align,
20
+ ...style,
21
+ },
112
22
  ...rest,
113
- };
114
- // Use React.createElement to avoid JSX generic complexity
115
- return React.createElement(Element, props, children);
23
+ }, children);
116
24
  }
117
25
  export default Text;
@@ -6,9 +6,8 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
6
6
  loading?: boolean;
7
7
  loadingText?: string;
8
8
  fullWidth?: boolean;
9
- /** 🎨 Theme */
10
- primaryTheme?: boolean;
11
- primaryColor?: string;
9
+ /** 🎨 Variants (shadcn style) */
10
+ variant?: "default" | "outline" | "ghost";
12
11
  /** 📏 Sizes */
13
12
  size?: "sm" | "md" | "lg";
14
13
  className?: string;