@cyberbloxai/ui-kit 0.1.1 → 0.2.0
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/README.md +62 -6
- package/dist/favicon.ico +0 -0
- package/package.json +12 -6
- package/src/App.css +42 -0
- package/src/App.tsx +31 -0
- package/src/cli.js +93 -0
- package/src/components/CodeBlock.tsx +50 -0
- package/src/components/ComponentLivePreview.tsx +310 -0
- package/src/components/NavLink.tsx +28 -0
- package/src/components/PropTable.tsx +54 -0
- package/src/components/showcase/AuthShowcase.tsx +164 -0
- package/src/components/showcase/ComponentsShowcase.tsx +133 -0
- package/src/components/showcase/DashboardShowcase.tsx +153 -0
- package/src/components/showcase/ErrorShowcase.tsx +55 -0
- package/src/components/showcase/NotificationShowcase.tsx +102 -0
- package/src/components/ui/accordion.tsx +52 -0
- package/src/components/ui/alert-dialog.tsx +104 -0
- package/src/components/ui/alert.tsx +43 -0
- package/src/components/ui/aspect-ratio.tsx +5 -0
- package/src/components/ui/avatar.tsx +38 -0
- package/src/components/ui/badge.tsx +29 -0
- package/src/components/ui/breadcrumb.tsx +90 -0
- package/src/components/ui/button.tsx +47 -0
- package/src/components/ui/calendar.tsx +54 -0
- package/src/components/ui/card.tsx +43 -0
- package/src/components/ui/carousel.tsx +224 -0
- package/src/components/ui/chart.tsx +303 -0
- package/src/components/ui/checkbox.tsx +26 -0
- package/src/components/ui/collapsible.tsx +9 -0
- package/src/components/ui/command.tsx +132 -0
- package/src/components/ui/context-menu.tsx +178 -0
- package/src/components/ui/dialog.tsx +95 -0
- package/src/components/ui/drawer.tsx +87 -0
- package/src/components/ui/dropdown-menu.tsx +179 -0
- package/src/components/ui/form.tsx +129 -0
- package/src/components/ui/hover-card.tsx +27 -0
- package/src/components/ui/input-otp.tsx +61 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/label.tsx +17 -0
- package/src/components/ui/menubar.tsx +207 -0
- package/src/components/ui/navigation-menu.tsx +120 -0
- package/src/components/ui/pagination.tsx +81 -0
- package/src/components/ui/popover.tsx +29 -0
- package/src/components/ui/progress.tsx +23 -0
- package/src/components/ui/radio-group.tsx +36 -0
- package/src/components/ui/resizable.tsx +37 -0
- package/src/components/ui/scroll-area.tsx +38 -0
- package/src/components/ui/select.tsx +143 -0
- package/src/components/ui/separator.tsx +20 -0
- package/src/components/ui/sheet.tsx +107 -0
- package/src/components/ui/sidebar.tsx +637 -0
- package/src/components/ui/skeleton.tsx +7 -0
- package/src/components/ui/slider.tsx +23 -0
- package/src/components/ui/sonner.tsx +27 -0
- package/src/components/ui/switch.tsx +27 -0
- package/src/components/ui/table.tsx +72 -0
- package/src/components/ui/tabs.tsx +53 -0
- package/src/components/ui/textarea.tsx +21 -0
- package/src/components/ui/toast.tsx +111 -0
- package/src/components/ui/toaster.tsx +24 -0
- package/src/components/ui/toggle-group.tsx +49 -0
- package/src/components/ui/toggle.tsx +37 -0
- package/src/components/ui/tooltip.tsx +28 -0
- package/src/components/ui/use-toast.ts +3 -0
- package/src/data/componentRegistry.ts +501 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/hooks/use-toast.ts +186 -0
- package/src/index.css +105 -0
- package/src/lib/index.ts +58 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +5 -0
- package/src/pages/ComponentDetail.tsx +167 -0
- package/src/pages/ComponentsList.tsx +126 -0
- package/src/pages/Index.tsx +223 -0
- package/src/pages/NotFound.tsx +24 -0
- package/src/test/example.test.ts +7 -0
- package/src/test/setup.ts +15 -0
- package/src/vite-env.d.ts +1 -0
package/README.md
CHANGED
|
@@ -18,32 +18,88 @@ CyberBlox UI Kit is a modular, themeable component library designed for building
|
|
|
18
18
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
|
21
|
+
There are two ways to use CyberBlox UI:
|
|
22
|
+
|
|
23
|
+
### 1. shadcn-style (Recommended)
|
|
24
|
+
This copies the component source code directly into your project, allowing you to customize everything.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# 1. Initialize the project (sets up utils)
|
|
28
|
+
npx @cyberbloxai/ui-kit init
|
|
29
|
+
|
|
30
|
+
# 2. Add components as needed
|
|
31
|
+
npx @cyberbloxai/ui-kit add button
|
|
32
|
+
npx @cyberbloxai/ui-kit add dialog
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Standard Library Style
|
|
36
|
+
Install it as a traditional npm package.
|
|
37
|
+
|
|
21
38
|
```bash
|
|
22
39
|
npm install @cyberbloxai/ui-kit
|
|
23
40
|
```
|
|
24
41
|
|
|
25
|
-
|
|
42
|
+
> **Note**: If you are using **Tailwind CSS v4** and encounter a dependency conflict (`ERESOLVE`), you can install using:
|
|
43
|
+
> `npm install @cyberbloxai/ui-kit --legacy-peer-deps`
|
|
26
44
|
|
|
27
|
-
|
|
45
|
+
## Quick Start (Library Style)
|
|
46
|
+
|
|
47
|
+
1. **Import Styles**: Add the CSS to your main entry file (e.g., `main.tsx` or `App.tsx`) **before** your other imports:
|
|
28
48
|
|
|
29
49
|
```tsx
|
|
30
50
|
import "@cyberbloxai/ui-kit/styles.css";
|
|
31
51
|
```
|
|
32
52
|
|
|
33
|
-
2. **
|
|
53
|
+
2. **Tailwind CSS Configuration**:
|
|
54
|
+
|
|
55
|
+
### For Tailwind CSS v4 (Recommended)
|
|
56
|
+
In your main CSS file (e.g., `index.css` or `app.css`), ensure you have:
|
|
57
|
+
```css
|
|
58
|
+
@import "tailwindcss";
|
|
59
|
+
/* If your components are unstyled, you may need to import the library CSS here instead */
|
|
60
|
+
@import "@cyberbloxai/ui-kit/styles.css";
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### For Tailwind CSS v3
|
|
64
|
+
In your `tailwind.config.js`, you **must** add the library to the `content` array so Tailwind can find the classes used in the components:
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
/** @type {import('tailwindcss').Config} */
|
|
68
|
+
export default {
|
|
69
|
+
content: [
|
|
70
|
+
"./index.html",
|
|
71
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
72
|
+
"./node_modules/@cyberbloxai/ui-kit/dist/**/*.{js,mjs}" // Add this line
|
|
73
|
+
],
|
|
74
|
+
// ... rest of config
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
3. **Use Components**:
|
|
34
79
|
|
|
35
80
|
```tsx
|
|
36
81
|
import { Button } from "@cyberbloxai/ui-kit";
|
|
37
82
|
|
|
38
83
|
function App() {
|
|
39
84
|
return (
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
85
|
+
<div className="p-8">
|
|
86
|
+
<Button variant="default" onClick={() => console.log("Clicked!")}>
|
|
87
|
+
Click Me
|
|
88
|
+
</Button>
|
|
89
|
+
</div>
|
|
43
90
|
);
|
|
44
91
|
}
|
|
45
92
|
```
|
|
46
93
|
|
|
94
|
+
## Troubleshooting
|
|
95
|
+
|
|
96
|
+
- **CLI Not Working**: If `npx @cyberbloxai/ui-kit` fails, try `npx @cyberbloxai/ui-kit@latest`.
|
|
97
|
+
- **Blank Page / White Screen**: This usually indicates a JavaScript error. Check your browser console (`F12` -> Console).
|
|
98
|
+
- Ensure you have `react` and `react-dom` installed.
|
|
99
|
+
- If using Vite, try clearing your cache: `rm -rf node_modules/.vite` and restart with `npm run dev`.
|
|
100
|
+
- **Unstyled Components**: Ensure you have imported `@cyberbloxai/ui-kit/styles.css` and configured your Tailwind `content` (for v3) or `@import` (for v4).
|
|
101
|
+
|
|
102
|
+
|
|
47
103
|
## Technologies Used
|
|
48
104
|
|
|
49
105
|
- **Vite**: Next-generation frontend tooling.
|
package/dist/favicon.ico
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyberbloxai/ui-kit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Production-ready UI component library built on Radix UI + Tailwind CSS.",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Production-ready UI component library built on Radix UI + Tailwind CSS. Supports shadcn-style component installation.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"cyberblox": "./src/cli.js"
|
|
7
|
+
},
|
|
5
8
|
"repository": {
|
|
6
9
|
"type": "git",
|
|
7
10
|
"url": "git+https://github.com/abhishekjohn1507/uni-kit-forge.git"
|
|
@@ -23,13 +26,16 @@
|
|
|
23
26
|
},
|
|
24
27
|
"files": [
|
|
25
28
|
"dist",
|
|
29
|
+
"src",
|
|
26
30
|
"README.md"
|
|
27
31
|
],
|
|
28
|
-
"sideEffects":
|
|
32
|
+
"sideEffects": [
|
|
33
|
+
"**/*.css"
|
|
34
|
+
],
|
|
29
35
|
"peerDependencies": {
|
|
30
|
-
"react": "^18.0.0",
|
|
31
|
-
"react-dom": "^18.0.0",
|
|
32
|
-
"tailwindcss": "^3.0.0"
|
|
36
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
37
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
38
|
+
"tailwindcss": "^3.0.0 || ^4.0.0"
|
|
33
39
|
},
|
|
34
40
|
"scripts": {
|
|
35
41
|
"dev": "vite",
|
package/src/App.css
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#root {
|
|
2
|
+
max-width: 1280px;
|
|
3
|
+
margin: 0 auto;
|
|
4
|
+
padding: 2rem;
|
|
5
|
+
text-align: center;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.logo {
|
|
9
|
+
height: 6em;
|
|
10
|
+
padding: 1.5em;
|
|
11
|
+
will-change: filter;
|
|
12
|
+
transition: filter 300ms;
|
|
13
|
+
}
|
|
14
|
+
.logo:hover {
|
|
15
|
+
filter: drop-shadow(0 0 2em #646cffaa);
|
|
16
|
+
}
|
|
17
|
+
.logo.react:hover {
|
|
18
|
+
filter: drop-shadow(0 0 2em #61dafbaa);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@keyframes logo-spin {
|
|
22
|
+
from {
|
|
23
|
+
transform: rotate(0deg);
|
|
24
|
+
}
|
|
25
|
+
to {
|
|
26
|
+
transform: rotate(360deg);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
31
|
+
a:nth-of-type(2) .logo {
|
|
32
|
+
animation: logo-spin infinite 20s linear;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.card {
|
|
37
|
+
padding: 2em;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.read-the-docs {
|
|
41
|
+
color: #888;
|
|
42
|
+
}
|
package/src/App.tsx
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Toaster } from "@/components/ui/toaster";
|
|
2
|
+
import { Toaster as Sonner } from "@/components/ui/sonner";
|
|
3
|
+
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
4
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
5
|
+
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
|
6
|
+
import Index from "./pages/Index";
|
|
7
|
+
import ComponentsList from "./pages/ComponentsList";
|
|
8
|
+
import ComponentDetail from "./pages/ComponentDetail";
|
|
9
|
+
import NotFound from "./pages/NotFound";
|
|
10
|
+
|
|
11
|
+
const queryClient = new QueryClient();
|
|
12
|
+
|
|
13
|
+
const App = () => (
|
|
14
|
+
<QueryClientProvider client={queryClient}>
|
|
15
|
+
<TooltipProvider>
|
|
16
|
+
<Toaster />
|
|
17
|
+
<Sonner />
|
|
18
|
+
<BrowserRouter>
|
|
19
|
+
<Routes>
|
|
20
|
+
<Route path="/" element={<Index />} />
|
|
21
|
+
<Route path="/components" element={<ComponentsList />} />
|
|
22
|
+
<Route path="/components/:id" element={<ComponentDetail />} />
|
|
23
|
+
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
|
|
24
|
+
<Route path="*" element={<NotFound />} />
|
|
25
|
+
</Routes>
|
|
26
|
+
</BrowserRouter>
|
|
27
|
+
</TooltipProvider>
|
|
28
|
+
</QueryClientProvider>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export default App;
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
|
|
10
|
+
// This script will be located in node_modules/@cyberbloxai/ui-kit/src/cli.js
|
|
11
|
+
// We need to find the source components directory relative to this script.
|
|
12
|
+
const packageRoot = path.join(__dirname, '..');
|
|
13
|
+
const componentsSrc = path.join(packageRoot, 'src', 'components', 'ui');
|
|
14
|
+
const utilsSrc = path.join(packageRoot, 'src', 'lib', 'utils.ts');
|
|
15
|
+
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const command = args[0];
|
|
18
|
+
|
|
19
|
+
function getTargetDir() {
|
|
20
|
+
const isSrcExists = fs.existsSync(path.join(process.cwd(), 'src'));
|
|
21
|
+
return isSrcExists ? path.join(process.cwd(), 'src') : process.cwd();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function copyFile(src, dest) {
|
|
25
|
+
const destDir = path.dirname(dest);
|
|
26
|
+
if (!fs.existsSync(destDir)) {
|
|
27
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
fs.copyFileSync(src, dest);
|
|
30
|
+
console.log(` Created ${path.relative(process.cwd(), dest)}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function init() {
|
|
34
|
+
console.log('Initializing CyberBlox UI components...');
|
|
35
|
+
const targetBase = getTargetDir();
|
|
36
|
+
|
|
37
|
+
// Copy utils
|
|
38
|
+
const utilsDest = path.join(targetBase, 'lib', 'utils.ts');
|
|
39
|
+
copyFile(utilsSrc, utilsDest);
|
|
40
|
+
|
|
41
|
+
console.log('\nSuccess! Now you can add components using:');
|
|
42
|
+
console.log('npx cyberblox add [component-name]');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function addComponent(name) {
|
|
46
|
+
if (!name) {
|
|
47
|
+
console.error('Please specify a component name, e.g., npx cyberblox add button');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const fileName = name.endsWith('.tsx') ? name : `${name}.tsx`;
|
|
52
|
+
const srcPath = path.join(componentsSrc, fileName);
|
|
53
|
+
|
|
54
|
+
if (!fs.existsSync(srcPath)) {
|
|
55
|
+
console.error(`Component "${name}" not found in library.`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const targetBase = getTargetDir();
|
|
60
|
+
const destPath = path.join(targetBase, 'components', 'ui', fileName);
|
|
61
|
+
|
|
62
|
+
copyFile(srcPath, destPath);
|
|
63
|
+
console.log(`\nComponent "${name}" added successfully!`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function listComponents() {
|
|
67
|
+
console.log('Available components:');
|
|
68
|
+
const files = fs.readdirSync(componentsSrc);
|
|
69
|
+
files.forEach(file => {
|
|
70
|
+
if (file.endsWith('.tsx')) {
|
|
71
|
+
console.log(`- ${file.replace('.tsx', '')}`);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
switch (command) {
|
|
77
|
+
case 'init':
|
|
78
|
+
init();
|
|
79
|
+
break;
|
|
80
|
+
case 'add':
|
|
81
|
+
addComponent(args[1]);
|
|
82
|
+
break;
|
|
83
|
+
case 'list':
|
|
84
|
+
listComponents();
|
|
85
|
+
break;
|
|
86
|
+
default:
|
|
87
|
+
console.log('CyberBlox UI CLI');
|
|
88
|
+
console.log('\nUsage:');
|
|
89
|
+
console.log(' npx cyberblox init - Setup lib/utils.ts');
|
|
90
|
+
console.log(' npx cyberblox add [name] - Add a component to your project');
|
|
91
|
+
console.log(' npx cyberblox list - List all available components');
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Check, Copy } from "lucide-react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
interface CodeBlockProps {
|
|
6
|
+
code: string;
|
|
7
|
+
language?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const CodeBlock = ({ code, language = "tsx", className }: CodeBlockProps) => {
|
|
12
|
+
const [copied, setCopied] = useState(false);
|
|
13
|
+
|
|
14
|
+
const handleCopy = () => {
|
|
15
|
+
navigator.clipboard.writeText(code);
|
|
16
|
+
setCopied(true);
|
|
17
|
+
setTimeout(() => setCopied(false), 2000);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className={cn("relative group", className)}>
|
|
22
|
+
<div className="flex items-center justify-between px-4 py-2 bg-muted/80 border border-border/40 rounded-t-lg">
|
|
23
|
+
<span className="text-xs font-mono text-muted-foreground">{language}</span>
|
|
24
|
+
<button
|
|
25
|
+
onClick={handleCopy}
|
|
26
|
+
className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
|
27
|
+
>
|
|
28
|
+
{copied ? (
|
|
29
|
+
<>
|
|
30
|
+
<Check className="h-3.5 w-3.5 text-success" />
|
|
31
|
+
<span>Copied</span>
|
|
32
|
+
</>
|
|
33
|
+
) : (
|
|
34
|
+
<>
|
|
35
|
+
<Copy className="h-3.5 w-3.5" />
|
|
36
|
+
<span>Copy</span>
|
|
37
|
+
</>
|
|
38
|
+
)}
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
41
|
+
<pre className="overflow-x-auto p-4 bg-background/80 border border-t-0 border-border/40 rounded-b-lg">
|
|
42
|
+
<code className="text-sm font-mono text-foreground/90 leading-relaxed whitespace-pre">
|
|
43
|
+
{code}
|
|
44
|
+
</code>
|
|
45
|
+
</pre>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default CodeBlock;
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Button } from "@/components/ui/button";
|
|
3
|
+
import { Input } from "@/components/ui/input";
|
|
4
|
+
import { Badge } from "@/components/ui/badge";
|
|
5
|
+
import { Switch } from "@/components/ui/switch";
|
|
6
|
+
import { Slider } from "@/components/ui/slider";
|
|
7
|
+
import { Checkbox } from "@/components/ui/checkbox";
|
|
8
|
+
import { Progress } from "@/components/ui/progress";
|
|
9
|
+
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
10
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
11
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
12
|
+
import { Label } from "@/components/ui/label";
|
|
13
|
+
import {
|
|
14
|
+
Dialog, DialogTrigger, DialogContent,
|
|
15
|
+
DialogHeader, DialogTitle, DialogDescription,
|
|
16
|
+
DialogFooter, DialogClose,
|
|
17
|
+
} from "@/components/ui/dialog";
|
|
18
|
+
import {
|
|
19
|
+
Drawer, DrawerTrigger, DrawerContent,
|
|
20
|
+
DrawerHeader, DrawerTitle, DrawerDescription,
|
|
21
|
+
DrawerFooter, DrawerClose,
|
|
22
|
+
} from "@/components/ui/drawer";
|
|
23
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
|
24
|
+
import {
|
|
25
|
+
Select, SelectTrigger, SelectValue, SelectContent,
|
|
26
|
+
SelectItem, SelectGroup, SelectLabel,
|
|
27
|
+
} from "@/components/ui/select";
|
|
28
|
+
import {
|
|
29
|
+
Accordion, AccordionItem, AccordionTrigger, AccordionContent,
|
|
30
|
+
} from "@/components/ui/accordion";
|
|
31
|
+
import {
|
|
32
|
+
Tooltip, TooltipTrigger, TooltipContent, TooltipProvider,
|
|
33
|
+
} from "@/components/ui/tooltip";
|
|
34
|
+
import { Loader2, ArrowRight, Plus, Settings, Info } from "lucide-react";
|
|
35
|
+
|
|
36
|
+
interface Props {
|
|
37
|
+
componentId: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ComponentLivePreview = ({ componentId }: Props) => {
|
|
41
|
+
const [sliderVal, setSliderVal] = useState([50]);
|
|
42
|
+
|
|
43
|
+
switch (componentId) {
|
|
44
|
+
case "button":
|
|
45
|
+
return (
|
|
46
|
+
<div className="flex flex-wrap gap-3">
|
|
47
|
+
<Button>Primary</Button>
|
|
48
|
+
<Button variant="secondary">Secondary</Button>
|
|
49
|
+
<Button variant="outline">Outline</Button>
|
|
50
|
+
<Button variant="destructive">Destructive</Button>
|
|
51
|
+
<Button variant="ghost">Ghost</Button>
|
|
52
|
+
<Button variant="link">Link</Button>
|
|
53
|
+
<Button disabled><Loader2 className="mr-2 h-4 w-4 animate-spin" />Loading</Button>
|
|
54
|
+
<Button size="icon"><ArrowRight /></Button>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
case "input":
|
|
59
|
+
return (
|
|
60
|
+
<div className="space-y-3 max-w-sm">
|
|
61
|
+
<Input placeholder="Default input" />
|
|
62
|
+
<Input type="email" placeholder="Email input" />
|
|
63
|
+
<Input placeholder="Disabled" disabled />
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
case "badge":
|
|
68
|
+
return (
|
|
69
|
+
<div className="flex flex-wrap gap-2">
|
|
70
|
+
<Badge>Default</Badge>
|
|
71
|
+
<Badge variant="secondary">Secondary</Badge>
|
|
72
|
+
<Badge variant="destructive">Destructive</Badge>
|
|
73
|
+
<Badge variant="outline">Outline</Badge>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
case "switch":
|
|
78
|
+
return (
|
|
79
|
+
<div className="space-y-3">
|
|
80
|
+
<div className="flex items-center gap-3">
|
|
81
|
+
<Switch id="s1" />
|
|
82
|
+
<Label htmlFor="s1">Off by default</Label>
|
|
83
|
+
</div>
|
|
84
|
+
<div className="flex items-center gap-3">
|
|
85
|
+
<Switch id="s2" defaultChecked />
|
|
86
|
+
<Label htmlFor="s2">On by default</Label>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
case "slider":
|
|
92
|
+
return (
|
|
93
|
+
<div className="space-y-4 max-w-sm">
|
|
94
|
+
<div className="space-y-2">
|
|
95
|
+
<div className="flex justify-between text-xs text-muted-foreground">
|
|
96
|
+
<span>Volume</span>
|
|
97
|
+
<span>{sliderVal[0]}%</span>
|
|
98
|
+
</div>
|
|
99
|
+
<Slider value={sliderVal} onValueChange={setSliderVal} max={100} step={1} />
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
case "checkbox":
|
|
105
|
+
return (
|
|
106
|
+
<div className="space-y-3">
|
|
107
|
+
{["Design system", "Components", "Documentation"].map((label) => (
|
|
108
|
+
<div key={label} className="flex items-center gap-2">
|
|
109
|
+
<Checkbox id={label} />
|
|
110
|
+
<Label htmlFor={label}>{label}</Label>
|
|
111
|
+
</div>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
case "progress":
|
|
117
|
+
return (
|
|
118
|
+
<div className="space-y-4 max-w-sm">
|
|
119
|
+
<Progress value={30} />
|
|
120
|
+
<Progress value={60} />
|
|
121
|
+
<Progress value={90} />
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
case "avatar":
|
|
126
|
+
return (
|
|
127
|
+
<div className="flex items-center gap-4">
|
|
128
|
+
<div className="flex -space-x-2">
|
|
129
|
+
{["AK", "BR", "CS", "DW"].map((i) => (
|
|
130
|
+
<Avatar key={i} className="h-10 w-10 border-2 border-background">
|
|
131
|
+
<AvatarFallback className="bg-primary/20 text-primary text-xs">{i}</AvatarFallback>
|
|
132
|
+
</Avatar>
|
|
133
|
+
))}
|
|
134
|
+
</div>
|
|
135
|
+
<Avatar className="h-14 w-14">
|
|
136
|
+
<AvatarFallback className="bg-accent/20 text-accent text-lg">JD</AvatarFallback>
|
|
137
|
+
</Avatar>
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
case "textarea":
|
|
142
|
+
return (
|
|
143
|
+
<div className="space-y-2 max-w-sm">
|
|
144
|
+
<Label htmlFor="bio">Bio</Label>
|
|
145
|
+
<Textarea id="bio" placeholder="Tell us about yourself..." rows={4} />
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
case "skeleton":
|
|
150
|
+
return (
|
|
151
|
+
<div className="flex items-center gap-4">
|
|
152
|
+
<Skeleton className="h-12 w-12 rounded-full" />
|
|
153
|
+
<div className="space-y-2">
|
|
154
|
+
<Skeleton className="h-4 w-[200px]" />
|
|
155
|
+
<Skeleton className="h-4 w-[150px]" />
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
case "label":
|
|
161
|
+
return (
|
|
162
|
+
<div className="space-y-2 max-w-sm">
|
|
163
|
+
<Label htmlFor="demo">Full Name</Label>
|
|
164
|
+
<Input id="demo" placeholder="John Doe" />
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
case "dialog":
|
|
169
|
+
return (
|
|
170
|
+
<Dialog>
|
|
171
|
+
<DialogTrigger asChild>
|
|
172
|
+
<Button variant="outline">Open Dialog</Button>
|
|
173
|
+
</DialogTrigger>
|
|
174
|
+
<DialogContent>
|
|
175
|
+
<DialogHeader>
|
|
176
|
+
<DialogTitle>Are you sure?</DialogTitle>
|
|
177
|
+
<DialogDescription>This action cannot be undone.</DialogDescription>
|
|
178
|
+
</DialogHeader>
|
|
179
|
+
<DialogFooter>
|
|
180
|
+
<DialogClose asChild>
|
|
181
|
+
<Button variant="outline">Cancel</Button>
|
|
182
|
+
</DialogClose>
|
|
183
|
+
<Button>Confirm</Button>
|
|
184
|
+
</DialogFooter>
|
|
185
|
+
</DialogContent>
|
|
186
|
+
</Dialog>
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
case "drawer":
|
|
190
|
+
return (
|
|
191
|
+
<Drawer>
|
|
192
|
+
<DrawerTrigger asChild>
|
|
193
|
+
<Button variant="outline">Open Drawer</Button>
|
|
194
|
+
</DrawerTrigger>
|
|
195
|
+
<DrawerContent>
|
|
196
|
+
<DrawerHeader>
|
|
197
|
+
<DrawerTitle>Settings</DrawerTitle>
|
|
198
|
+
<DrawerDescription>Adjust your preferences below.</DrawerDescription>
|
|
199
|
+
</DrawerHeader>
|
|
200
|
+
<div className="p-4 space-y-3">
|
|
201
|
+
<div className="flex items-center justify-between">
|
|
202
|
+
<Label>Notifications</Label>
|
|
203
|
+
<Switch />
|
|
204
|
+
</div>
|
|
205
|
+
<div className="flex items-center justify-between">
|
|
206
|
+
<Label>Dark Mode</Label>
|
|
207
|
+
<Switch defaultChecked />
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
<DrawerFooter>
|
|
211
|
+
<Button>Save Changes</Button>
|
|
212
|
+
<DrawerClose asChild>
|
|
213
|
+
<Button variant="outline">Cancel</Button>
|
|
214
|
+
</DrawerClose>
|
|
215
|
+
</DrawerFooter>
|
|
216
|
+
</DrawerContent>
|
|
217
|
+
</Drawer>
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
case "tabs":
|
|
221
|
+
return (
|
|
222
|
+
<Tabs defaultValue="overview" className="w-full max-w-md">
|
|
223
|
+
<TabsList className="grid w-full grid-cols-3">
|
|
224
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
225
|
+
<TabsTrigger value="analytics">Analytics</TabsTrigger>
|
|
226
|
+
<TabsTrigger value="settings">Settings</TabsTrigger>
|
|
227
|
+
</TabsList>
|
|
228
|
+
<TabsContent value="overview" className="mt-3 text-sm text-muted-foreground">
|
|
229
|
+
<p>Here's a quick summary of your project status and recent activity.</p>
|
|
230
|
+
</TabsContent>
|
|
231
|
+
<TabsContent value="analytics" className="mt-3 text-sm text-muted-foreground">
|
|
232
|
+
<p>View charts and metrics about your usage and performance.</p>
|
|
233
|
+
</TabsContent>
|
|
234
|
+
<TabsContent value="settings" className="mt-3 text-sm text-muted-foreground">
|
|
235
|
+
<p>Configure notifications, integrations, and account preferences.</p>
|
|
236
|
+
</TabsContent>
|
|
237
|
+
</Tabs>
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
case "select":
|
|
241
|
+
return (
|
|
242
|
+
<div className="space-y-3 max-w-[220px]">
|
|
243
|
+
<Label>Favorite fruit</Label>
|
|
244
|
+
<Select>
|
|
245
|
+
<SelectTrigger>
|
|
246
|
+
<SelectValue placeholder="Select a fruit" />
|
|
247
|
+
</SelectTrigger>
|
|
248
|
+
<SelectContent>
|
|
249
|
+
<SelectGroup>
|
|
250
|
+
<SelectLabel>Fruits</SelectLabel>
|
|
251
|
+
<SelectItem value="apple">Apple</SelectItem>
|
|
252
|
+
<SelectItem value="banana">Banana</SelectItem>
|
|
253
|
+
<SelectItem value="orange">Orange</SelectItem>
|
|
254
|
+
<SelectItem value="grape">Grape</SelectItem>
|
|
255
|
+
</SelectGroup>
|
|
256
|
+
</SelectContent>
|
|
257
|
+
</Select>
|
|
258
|
+
</div>
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
case "accordion":
|
|
262
|
+
return (
|
|
263
|
+
<Accordion type="single" collapsible className="w-full max-w-md">
|
|
264
|
+
<AccordionItem value="item-1">
|
|
265
|
+
<AccordionTrigger>Is it accessible?</AccordionTrigger>
|
|
266
|
+
<AccordionContent>Yes. It adheres to the WAI-ARIA design pattern.</AccordionContent>
|
|
267
|
+
</AccordionItem>
|
|
268
|
+
<AccordionItem value="item-2">
|
|
269
|
+
<AccordionTrigger>Is it styled?</AccordionTrigger>
|
|
270
|
+
<AccordionContent>Yes. It comes with default styles that match the design system.</AccordionContent>
|
|
271
|
+
</AccordionItem>
|
|
272
|
+
<AccordionItem value="item-3">
|
|
273
|
+
<AccordionTrigger>Is it animated?</AccordionTrigger>
|
|
274
|
+
<AccordionContent>Yes. It uses CSS animations for smooth open and close transitions.</AccordionContent>
|
|
275
|
+
</AccordionItem>
|
|
276
|
+
</Accordion>
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
case "tooltip":
|
|
280
|
+
return (
|
|
281
|
+
<TooltipProvider>
|
|
282
|
+
<div className="flex flex-wrap gap-3">
|
|
283
|
+
<Tooltip>
|
|
284
|
+
<TooltipTrigger asChild>
|
|
285
|
+
<Button variant="outline" size="icon"><Plus className="h-4 w-4" /></Button>
|
|
286
|
+
</TooltipTrigger>
|
|
287
|
+
<TooltipContent><p>Add item</p></TooltipContent>
|
|
288
|
+
</Tooltip>
|
|
289
|
+
<Tooltip>
|
|
290
|
+
<TooltipTrigger asChild>
|
|
291
|
+
<Button variant="outline" size="icon"><Settings className="h-4 w-4" /></Button>
|
|
292
|
+
</TooltipTrigger>
|
|
293
|
+
<TooltipContent side="bottom"><p>Settings</p></TooltipContent>
|
|
294
|
+
</Tooltip>
|
|
295
|
+
<Tooltip>
|
|
296
|
+
<TooltipTrigger asChild>
|
|
297
|
+
<Button variant="outline" size="icon"><Info className="h-4 w-4" /></Button>
|
|
298
|
+
</TooltipTrigger>
|
|
299
|
+
<TooltipContent side="right"><p>More info</p></TooltipContent>
|
|
300
|
+
</Tooltip>
|
|
301
|
+
</div>
|
|
302
|
+
</TooltipProvider>
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
default:
|
|
306
|
+
return <p className="text-muted-foreground text-sm">No preview available.</p>;
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
export default ComponentLivePreview;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NavLink as RouterNavLink, NavLinkProps } from "react-router-dom";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
interface NavLinkCompatProps extends Omit<NavLinkProps, "className"> {
|
|
6
|
+
className?: string;
|
|
7
|
+
activeClassName?: string;
|
|
8
|
+
pendingClassName?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const NavLink = forwardRef<HTMLAnchorElement, NavLinkCompatProps>(
|
|
12
|
+
({ className, activeClassName, pendingClassName, to, ...props }, ref) => {
|
|
13
|
+
return (
|
|
14
|
+
<RouterNavLink
|
|
15
|
+
ref={ref}
|
|
16
|
+
to={to}
|
|
17
|
+
className={({ isActive, isPending }) =>
|
|
18
|
+
cn(className, isActive && activeClassName, isPending && pendingClassName)
|
|
19
|
+
}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
NavLink.displayName = "NavLink";
|
|
27
|
+
|
|
28
|
+
export { NavLink };
|