@mnee-ui/ui 0.0.1 → 0.0.2

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.
@@ -0,0 +1,2 @@
1
+ import { type ClassValue } from "clsx";
2
+ export declare function cn(...inputs: ClassValue[]): string;
package/package.json CHANGED
@@ -1,14 +1,22 @@
1
1
  {
2
2
  "name": "@mnee-ui/ui",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "MNEE Design System — components and documentation",
5
5
  "license": "MIT",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/components/ui/index.d.ts",
6
9
  "exports": {
7
- ".": "./components/ui/index.ts",
10
+ ".": {
11
+ "types": "./dist/components/ui/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ },
8
15
  "./styles": "./components/ui/mnee-ui.css"
9
16
  },
10
17
  "files": [
11
- "components/ui/"
18
+ "dist/",
19
+ "components/ui/mnee-ui.css"
12
20
  ],
13
21
  "publishConfig": {
14
22
  "access": "public"
@@ -18,10 +26,13 @@
18
26
  "tailwindcss": ">=3"
19
27
  },
20
28
  "scripts": {
29
+ "build:lib": "tsup && tsc --project tsconfig.lib.json",
21
30
  "dev": "next dev",
22
31
  "build": "next build",
23
32
  "start": "next start",
24
- "lint": "next lint"
33
+ "lint": "next lint",
34
+ "figma:parse": "npx figma connect parse --dry-run",
35
+ "figma:publish": "npx figma connect publish"
25
36
  },
26
37
  "dependencies": {
27
38
  "@mdx-js/loader": "^3.1.1",
@@ -29,21 +40,23 @@
29
40
  "@next/mdx": "^16.1.6",
30
41
  "clsx": "^2.1.1",
31
42
  "lucide-react": "^0.575.0",
32
- "next": "15.2.4",
33
- "react": "^19.0.0",
43
+ "next": "^15.5.12",
44
+ "react": ">=18",
34
45
  "react-dom": "^19.0.0",
35
46
  "shiki": "^3.23.0",
36
47
  "tailwind-merge": "^3.5.0"
37
48
  },
38
49
  "devDependencies": {
39
50
  "@eslint/eslintrc": "^3",
51
+ "@figma/code-connect": "^1.4.1",
40
52
  "@tailwindcss/postcss": "^4",
41
53
  "@types/node": "^20",
42
54
  "@types/react": "^19",
43
55
  "@types/react-dom": "^19",
44
56
  "eslint": "^9",
45
- "eslint-config-next": "15.2.4",
57
+ "eslint-config-next": "^15.5.12",
46
58
  "tailwindcss": "^4",
59
+ "tsup": "^8.5.1",
47
60
  "typescript": "^5"
48
61
  }
49
62
  }
@@ -1,84 +0,0 @@
1
- "use client";
2
-
3
- import { Info, AlertTriangle, Lightbulb, CheckCircle } from "lucide-react";
4
- import { cn } from "@/lib/utils";
5
-
6
- export type AlertVariant = "info" | "warning" | "tip" | "error" | "success";
7
-
8
- export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
9
- variant?: AlertVariant;
10
- title?: string;
11
- children?: React.ReactNode;
12
- }
13
-
14
- const variantStyles: Record<AlertVariant, { wrapper: string; text: string }> = {
15
- info: {
16
- wrapper: "border-blue-800 bg-blue-50",
17
- text: "text-blue-800",
18
- },
19
- warning: {
20
- wrapper: "border-[#FFF085] bg-[#FEFCE8]",
21
- text: "text-[#A65F00]",
22
- },
23
- tip: {
24
- wrapper: "border-[#FFF085] bg-[#FEFCE8]",
25
- text: "text-[#A65F00]",
26
- },
27
- error: {
28
- wrapper: "border-error/40 bg-error-bg",
29
- text: "text-error",
30
- },
31
- success: {
32
- wrapper: "border-success/40 bg-success-bg",
33
- text: "text-success",
34
- },
35
- };
36
-
37
- const variantIcons: Record<AlertVariant, React.ElementType> = {
38
- info: Info,
39
- warning: AlertTriangle,
40
- tip: Lightbulb,
41
- error: AlertTriangle,
42
- success: CheckCircle,
43
- };
44
-
45
- const variantLabels: Record<AlertVariant, string> = {
46
- info: "Note",
47
- warning: "Warning",
48
- tip: "Tip",
49
- error: "Error",
50
- success: "Success",
51
- };
52
-
53
- export function Alert({
54
- variant = "info",
55
- title,
56
- children,
57
- className,
58
- ...props
59
- }: AlertProps) {
60
- const styles = variantStyles[variant];
61
- const Icon = variantIcons[variant];
62
- const label = title ?? variantLabels[variant];
63
-
64
- return (
65
- <div
66
- className={cn(
67
- "rounded-lg border px-4 py-2",
68
- styles.wrapper,
69
- className
70
- )}
71
- {...props}
72
- >
73
- <div className={cn("flex items-center gap-2 font-medium text-[12px]", styles.text)}>
74
- <Icon size={15} />
75
- <span>{label}</span>
76
- </div>
77
- {children && (
78
- <div className={cn("pl-6 font-light text-[12px]", styles.text)}>
79
- {children}
80
- </div>
81
- )}
82
- </div>
83
- );
84
- }
@@ -1,38 +0,0 @@
1
- import { cn } from "@/lib/utils";
2
-
3
- export type BadgeVariant = "success" | "warning" | "error" | "info" | "default" | "brand";
4
-
5
- export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
6
- variant?: BadgeVariant;
7
- children?: React.ReactNode;
8
- }
9
-
10
- const variantStyles: Record<BadgeVariant, string> = {
11
- success: "bg-success text-white",
12
- warning: "bg-warning text-white",
13
- error: "bg-red-600 text-white",
14
- info: "bg-info text-white",
15
- default: "bg-gray-600 text-white",
16
- brand: "bg-brand text-white",
17
- };
18
-
19
- export function Badge({
20
- variant = "default",
21
- className,
22
- children,
23
- ...props
24
- }: BadgeProps) {
25
- return (
26
- <span
27
- className={cn(
28
- "inline-flex items-center justify-center px-2.5 py-0.5 rounded-lg",
29
- "text-xs font-medium leading-4 whitespace-nowrap",
30
- variantStyles[variant],
31
- className
32
- )}
33
- {...props}
34
- >
35
- {children}
36
- </span>
37
- );
38
- }
@@ -1,73 +0,0 @@
1
- import { cn } from "@/lib/utils";
2
-
3
- export type BannerVariant = "gradient" | "info" | "success" | "warning" | "error";
4
-
5
- export interface BannerProps extends React.HTMLAttributes<HTMLDivElement> {
6
- title: string;
7
- description?: string;
8
- variant?: BannerVariant;
9
- action?: React.ReactNode;
10
- }
11
-
12
- const variantStyles: Record<BannerVariant, { wrapper: string; title: string; desc: string }> = {
13
- gradient: {
14
- wrapper: "border border-[var(--color-surface-border)]",
15
- title: "text-gray-800",
16
- desc: "text-gray-600",
17
- },
18
- info: {
19
- wrapper: "bg-info-bg border border-info/20",
20
- title: "text-info",
21
- desc: "text-info/80",
22
- },
23
- success: {
24
- wrapper: "bg-success-bg border border-success/20",
25
- title: "text-success",
26
- desc: "text-success/80",
27
- },
28
- warning: {
29
- wrapper: "bg-warning-bg border border-warning/20",
30
- title: "text-warning",
31
- desc: "text-warning/80",
32
- },
33
- error: {
34
- wrapper: "bg-error-bg border border-error/20",
35
- title: "text-error",
36
- desc: "text-error/80",
37
- },
38
- };
39
-
40
- export function Banner({
41
- title,
42
- description,
43
- variant = "gradient",
44
- action,
45
- className,
46
- ...props
47
- }: BannerProps) {
48
- const styles = variantStyles[variant];
49
-
50
- return (
51
- <div
52
- className={cn(
53
- "rounded-lg p-4 flex items-center justify-between gap-4 shadow-sm",
54
- styles.wrapper,
55
- className
56
- )}
57
- style={
58
- variant === "gradient"
59
- ? { background: "linear-gradient(90deg, #F0FDFA 0%, #FFF7ED 100%)" }
60
- : undefined
61
- }
62
- {...props}
63
- >
64
- <div className="flex flex-col gap-0.5">
65
- <p className={cn("font-semibold text-sm", styles.title)}>{title}</p>
66
- {description && (
67
- <p className={cn("text-sm", styles.desc)}>{description}</p>
68
- )}
69
- </div>
70
- {action && <div className="shrink-0">{action}</div>}
71
- </div>
72
- );
73
- }
@@ -1,59 +0,0 @@
1
- import { Loader2 } from "lucide-react";
2
- import { cn } from "@/lib/utils";
3
-
4
- export type ButtonVariant = "primary" | "secondary" | "destructive" | "ghost" | "outline";
5
- export type ButtonSize = "sm" | "md" | "lg";
6
-
7
- export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
8
- variant?: ButtonVariant;
9
- size?: ButtonSize;
10
- loading?: boolean;
11
- children?: React.ReactNode;
12
- }
13
-
14
- const variantStyles: Record<ButtonVariant, string> = {
15
- primary:
16
- "bg-brand text-white hover:bg-brand-dark active:bg-brand-dark shadow-sm",
17
- secondary:
18
- "bg-gray-100 text-gray-900 hover:bg-gray-200 active:bg-gray-200",
19
- destructive:
20
- "bg-error text-white hover:opacity-90 active:opacity-90 shadow-sm",
21
- ghost:
22
- "bg-transparent text-gray-700 hover:bg-gray-100 active:bg-gray-100",
23
- outline:
24
- "bg-white border border-gray-300 text-gray-700 hover:bg-gray-50 active:bg-gray-100 shadow-sm",
25
- };
26
-
27
- const sizeStyles: Record<ButtonSize, string> = {
28
- sm: "h-7 px-3 text-xs rounded-md gap-1.5",
29
- md: "h-9 px-4 text-sm rounded-lg gap-2",
30
- lg: "h-11 px-6 text-base rounded-lg gap-2.5",
31
- };
32
-
33
- export function Button({
34
- variant = "primary",
35
- size = "md",
36
- loading = false,
37
- disabled,
38
- className,
39
- children,
40
- ...props
41
- }: ButtonProps) {
42
- return (
43
- <button
44
- className={cn(
45
- "inline-flex items-center justify-center font-medium transition-colors",
46
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/50",
47
- "disabled:pointer-events-none disabled:opacity-50",
48
- variantStyles[variant],
49
- sizeStyles[size],
50
- className
51
- )}
52
- disabled={disabled || loading}
53
- {...props}
54
- >
55
- {loading && <Loader2 className="animate-spin" size={size === "lg" ? 18 : 14} />}
56
- {children}
57
- </button>
58
- );
59
- }
@@ -1,156 +0,0 @@
1
- "use client";
2
-
3
- import { cn } from "@/lib/utils";
4
- import { Badge } from "./badge";
5
- import { Button } from "./button";
6
-
7
- /* ── Discriminated union ──────────────────────────────── */
8
-
9
- type BalanceCardProps = {
10
- variant: "balance";
11
- title: string;
12
- description?: string;
13
- amount: string;
14
- action?: React.ReactNode;
15
- loading?: boolean;
16
- className?: string;
17
- };
18
-
19
- type ModuleCardProps = {
20
- variant: "module";
21
- title: string;
22
- description?: string;
23
- status?: "success" | "warning" | "error" | "info" | "default";
24
- statusLabel?: string;
25
- onEdit?: () => void;
26
- onView?: () => void;
27
- loading?: boolean;
28
- className?: string;
29
- };
30
-
31
- export type CardProps = BalanceCardProps | ModuleCardProps;
32
-
33
- /* ── CardContainer — generic composable card ─────────── */
34
-
35
- export interface CardContainerProps extends React.HTMLAttributes<HTMLDivElement> {
36
- children: React.ReactNode;
37
- }
38
-
39
- export function CardContainer({ className, children, ...props }: CardContainerProps) {
40
- return (
41
- <div
42
- className={cn("bg-white rounded-lg border border-[#E5E5E5] shadow-sm", className)}
43
- {...props}
44
- >
45
- {children}
46
- </div>
47
- );
48
- }
49
-
50
- /* ── Card ─────────────────────────────────────────────── */
51
-
52
- export function Card(props: CardProps) {
53
- if (props.variant === "balance") {
54
- return <BalanceCard {...props} />;
55
- }
56
- return <ModuleCard {...props} />;
57
- }
58
-
59
- /* ── Internal: shell ──────────────────────────────────── */
60
-
61
- function CardShell({ className, children }: { className?: string; children: React.ReactNode }) {
62
- return (
63
- <div className={cn("bg-white rounded-lg border border-[#E5E5E5] shadow-sm", className)}>
64
- {children}
65
- </div>
66
- );
67
- }
68
-
69
- /* ── Internal: skeleton block ─────────────────────────── */
70
-
71
- function Skeleton({ className }: { className?: string }) {
72
- return <div className={cn("animate-pulse rounded bg-gray-200", className)} />;
73
- }
74
-
75
- /* ── Internal: BalanceCard ────────────────────────────── */
76
-
77
- function BalanceCard({ title, description, amount, action, loading, className }: BalanceCardProps) {
78
- return (
79
- <CardShell className={className}>
80
- <div className="px-6 pt-6 pb-4">
81
- {loading ? (
82
- <>
83
- <Skeleton className="h-4 w-32" />
84
- {description !== undefined && <Skeleton className="mt-2 h-3 w-48" />}
85
- </>
86
- ) : (
87
- <>
88
- <h3 className="text-base font-semibold text-gray-900 leading-tight">{title}</h3>
89
- {description && (
90
- <p className="mt-1 text-sm text-gray-500 leading-normal">{description}</p>
91
- )}
92
- </>
93
- )}
94
- </div>
95
- <div className="px-6 pb-4">
96
- {loading ? (
97
- <Skeleton className="h-9 w-36" />
98
- ) : (
99
- <p className="text-3xl font-bold text-gray-900">{amount}</p>
100
- )}
101
- </div>
102
- {action && (
103
- <div className="flex items-center px-6 py-4 border-t border-[#E5E5E5] bg-gray-50 rounded-b-lg">
104
- {action}
105
- </div>
106
- )}
107
- </CardShell>
108
- );
109
- }
110
-
111
- /* ── Internal: ModuleCard ─────────────────────────────── */
112
-
113
- function ModuleCard({ title, description, status, statusLabel, onEdit, onView, loading, className }: ModuleCardProps) {
114
- return (
115
- <CardShell className={className}>
116
- <div className="px-6 pt-6 pb-4">
117
- {loading ? (
118
- <>
119
- <div className="flex items-center justify-between">
120
- <Skeleton className="h-4 w-36" />
121
- <Skeleton className="h-5 w-16 rounded-full" />
122
- </div>
123
- {description !== undefined && <Skeleton className="mt-2 h-3 w-52" />}
124
- </>
125
- ) : (
126
- <>
127
- <div className="flex items-center justify-between">
128
- <h3 className="text-base font-semibold text-gray-900 leading-tight">{title}</h3>
129
- {status && statusLabel && (
130
- <Badge variant={status}>{statusLabel}</Badge>
131
- )}
132
- </div>
133
- {description && (
134
- <p className="mt-1 text-sm text-gray-500 leading-normal">{description}</p>
135
- )}
136
- </>
137
- )}
138
- </div>
139
- {(onEdit || onView) && (
140
- <div className="flex items-center justify-end gap-2 px-6 py-4 border-t border-[#E5E5E5] bg-gray-50 rounded-b-lg">
141
- {loading ? (
142
- <>
143
- <Skeleton className="h-7 w-12" />
144
- <Skeleton className="h-7 w-12" />
145
- </>
146
- ) : (
147
- <>
148
- {onEdit && <Button variant="ghost" size="sm" onClick={onEdit}>Edit</Button>}
149
- {onView && <Button variant="primary" size="sm" onClick={onView}>View</Button>}
150
- </>
151
- )}
152
- </div>
153
- )}
154
- </CardShell>
155
- );
156
- }
@@ -1,108 +0,0 @@
1
- "use client";
2
-
3
- import { useState, useEffect } from "react";
4
- import { Copy } from "lucide-react";
5
- import { cn } from "@/lib/utils";
6
- import { useToast } from "@/components/ui/toast";
7
- import type { ThemedToken } from "shiki";
8
-
9
- export interface CodeBlockProps {
10
- code: string;
11
- language?: string;
12
- title?: string;
13
- className?: string;
14
- }
15
-
16
- type TokenLine = ThemedToken[];
17
-
18
- export function CodeBlock({
19
- code,
20
- language = "bash",
21
- title,
22
- className,
23
- }: CodeBlockProps) {
24
- const [tokens, setTokens] = useState<TokenLine[] | null>(null);
25
- const [bg, setBg] = useState("#000000");
26
- const [fg, setFg] = useState("#d4d4d4");
27
- const [copied, setCopied] = useState(false);
28
- const { showToast } = useToast();
29
-
30
- useEffect(() => {
31
- const highlight = async () => {
32
- try {
33
- const { codeToTokens } = await import("shiki");
34
- const result = await codeToTokens(code.trim(), {
35
- lang: language as Parameters<typeof codeToTokens>[1]["lang"],
36
- theme: "dark-plus",
37
- });
38
- setTokens(result.tokens);
39
- if (result.bg) setBg(result.bg);
40
- if (result.fg) setFg(result.fg);
41
- } catch {
42
- // leave tokens null — fallback pre renders raw code
43
- }
44
- };
45
- highlight();
46
- }, [code, language]);
47
-
48
- const handleCopy = () => {
49
- navigator.clipboard.writeText(code);
50
- setCopied(true);
51
- showToast("Copied to clipboard!", "success");
52
- setTimeout(() => setCopied(false), 1500);
53
- };
54
-
55
- const copyButton = (
56
- <button
57
- onClick={handleCopy}
58
- className="flex items-center gap-1 bg-gray-700 hover:bg-gray-600 text-gray-200 px-2 py-1 rounded text-xs transition-colors"
59
- >
60
- <Copy size={13} />
61
- {copied ? "Copied!" : "Copy"}
62
- </button>
63
- );
64
-
65
- return (
66
- <div
67
- className={cn(
68
- "relative border border-gray-700 rounded-lg overflow-hidden",
69
- className
70
- )}
71
- >
72
- {title ? (
73
- <div className="flex items-center justify-between bg-[#161B22] px-4 py-2 text-sm text-gray-300 border-b border-gray-700">
74
- <span className="truncate">{title}</span>
75
- {copyButton}
76
- </div>
77
- ) : (
78
- <div className="absolute top-2 right-2 z-10">{copyButton}</div>
79
- )}
80
-
81
- {tokens === null ? (
82
- <pre
83
- className="p-4 overflow-x-auto text-sm font-mono"
84
- style={{ background: bg, color: fg }}
85
- >
86
- <code>{code}</code>
87
- </pre>
88
- ) : (
89
- <pre
90
- className="p-4 overflow-x-auto text-sm font-mono !m-0 !rounded-none"
91
- style={{ background: bg, color: fg }}
92
- >
93
- <code>
94
- {tokens.map((line, i) => (
95
- <span key={i} className="block">
96
- {line.map((token, j) => (
97
- <span key={j} style={{ color: token.color }}>
98
- {token.content}
99
- </span>
100
- ))}
101
- </span>
102
- ))}
103
- </code>
104
- </pre>
105
- )}
106
- </div>
107
- );
108
- }