modula-ui 1.0.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 +36 -0
- package/bin/run.js +86 -0
- package/package.json +71 -0
- package/public/avatars/avatar1.png +0 -0
- package/public/avatars/avatar2.png +0 -0
- package/public/avatars/avatar3.png +0 -0
- package/public/avatars/avatar4.png +0 -0
- package/public/avatars/sources.md +34 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/registry.json +12 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +126 -0
- package/src/app/layout.js +29 -0
- package/src/app/page.js +50 -0
- package/src/app/patterns/page.js +50 -0
- package/src/components/CodeCard.jsx +16 -0
- package/src/components/CopyButton.jsx +31 -0
- package/src/components/Header.jsx +24 -0
- package/src/components/Logo.jsx +64 -0
- package/src/components/MobileOverlay.jsx +10 -0
- package/src/components/PreviewCard.jsx +98 -0
- package/src/components/Sidebar.jsx +47 -0
- package/src/components/ui/avatar.jsx +47 -0
- package/src/components/ui/badge.jsx +44 -0
- package/src/components/ui/button.jsx +56 -0
- package/src/components/ui/calendar.jsx +178 -0
- package/src/components/ui/card.jsx +101 -0
- package/src/components/ui/chart.jsx +314 -0
- package/src/components/ui/checkbox.jsx +30 -0
- package/src/components/ui/dropdown-menu.jsx +223 -0
- package/src/components/ui/input.jsx +24 -0
- package/src/components/ui/navigation-menu.jsx +152 -0
- package/src/components/ui/popover.jsx +47 -0
- package/src/components/ui/progress.jsx +29 -0
- package/src/components/ui/scroll-area.jsx +51 -0
- package/src/components/ui/select.jsx +168 -0
- package/src/components/ui/separator.jsx +27 -0
- package/src/components/ui/sheet.jsx +140 -0
- package/src/components/ui/sidebar.jsx +682 -0
- package/src/components/ui/skeleton.jsx +15 -0
- package/src/components/ui/slider.jsx +56 -0
- package/src/components/ui/tooltip.jsx +55 -0
- package/src/data/componentData.js +12 -0
- package/src/hooks/use-mobile.js +19 -0
- package/src/lib/utils.js +6 -0
- package/src/library/components/Alert.jsx +27 -0
- package/src/library/components/Badge.jsx +19 -0
- package/src/library/components/Button.jsx +31 -0
- package/src/library/components/Card.jsx +25 -0
- package/src/library/components/Input.jsx +35 -0
- package/src/library/components/Modal.jsx +26 -0
- package/src/library/components/Textarea.jsx +15 -0
- package/src/library/components/Toggle.jsx +16 -0
- package/src/library/pages/FitnessPage/FitnessPage.jsx +519 -0
- package/src/library/pages/FitnessPage/index.jsx +12 -0
- package/src/library/pages/GroupChat/GroupChat.jsx +275 -0
- package/src/library/pages/GroupChat/data.js +203 -0
- package/src/library/pages/GroupChat/index.jsx +12 -0
- package/src/library/pages/ReservationsOverview/ReservationsOverviewPage.jsx +225 -0
- package/src/library/pages/ReservationsOverview/index.jsx +12 -0
- package/src/library/pages/VideoConference/VideoConferencePage.jsx +334 -0
- package/src/library/pages/VideoConference/index.jsx +12 -0
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
|
2
|
+
|
|
3
|
+
## Getting Started
|
|
4
|
+
https://grok.com/c/8a8910e9-3c12-4eb9-8136-d8650eadb428
|
|
5
|
+
First, run the development server:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev
|
|
9
|
+
# or
|
|
10
|
+
yarn dev
|
|
11
|
+
# or
|
|
12
|
+
pnpm dev
|
|
13
|
+
# or
|
|
14
|
+
bun dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
18
|
+
|
|
19
|
+
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
|
|
20
|
+
|
|
21
|
+
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
|
22
|
+
|
|
23
|
+
## Learn More
|
|
24
|
+
|
|
25
|
+
To learn more about Next.js, take a look at the following resources:
|
|
26
|
+
|
|
27
|
+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
28
|
+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
29
|
+
|
|
30
|
+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
31
|
+
|
|
32
|
+
## Deploy on Vercel
|
|
33
|
+
|
|
34
|
+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
35
|
+
|
|
36
|
+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
package/bin/run.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createServer } from 'http';
|
|
4
|
+
import next from 'next';
|
|
5
|
+
import { parse } from 'url';
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const packageRoot = path.join(__dirname, '..');
|
|
12
|
+
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const command = args[0];
|
|
15
|
+
|
|
16
|
+
if (command === 'add') {
|
|
17
|
+
const componentName = args[1];
|
|
18
|
+
|
|
19
|
+
if (!componentName) {
|
|
20
|
+
console.error('Please specify a component name: npx nora-ui add <component-name>');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const registryPath = path.join(packageRoot, 'registry.json');
|
|
26
|
+
const registryContent = await fs.readFile(registryPath, 'utf-8');
|
|
27
|
+
const registry = JSON.parse(registryContent);
|
|
28
|
+
|
|
29
|
+
const component = registry[componentName];
|
|
30
|
+
|
|
31
|
+
if (!component) {
|
|
32
|
+
console.error(`Component "${componentName}" not found in registry.`);
|
|
33
|
+
console.log('Available components:', Object.keys(registry).join(', '));
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const sourcePath = path.join(packageRoot, component.path);
|
|
38
|
+
|
|
39
|
+
// Determine destination path
|
|
40
|
+
// Check if src directory exists in the user's project
|
|
41
|
+
const userProjectRoot = process.cwd();
|
|
42
|
+
const hasSrc = await fs.access(path.join(userProjectRoot, 'src')).then(() => true).catch(() => false);
|
|
43
|
+
|
|
44
|
+
const destDir = hasSrc
|
|
45
|
+
? path.join(userProjectRoot, 'src', 'library', component.type)
|
|
46
|
+
: path.join(userProjectRoot, 'library', component.type);
|
|
47
|
+
|
|
48
|
+
const destPath = path.join(destDir, path.basename(sourcePath));
|
|
49
|
+
|
|
50
|
+
// Ensure destination directory exists
|
|
51
|
+
await fs.mkdir(destDir, { recursive: true });
|
|
52
|
+
|
|
53
|
+
// Read and write file
|
|
54
|
+
const content = await fs.readFile(sourcePath, 'utf-8');
|
|
55
|
+
await fs.writeFile(destPath, content);
|
|
56
|
+
|
|
57
|
+
console.log(`✓ Component ${component.name} added to ${path.relative(userProjectRoot, destPath)}`);
|
|
58
|
+
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('Error adding component:', error);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
} else {
|
|
65
|
+
// Default behavior: Start the Next.js server
|
|
66
|
+
const port = 3177;
|
|
67
|
+
const dev = false;
|
|
68
|
+
const app = next({ dev, dir: packageRoot });
|
|
69
|
+
const handle = app.getRequestHandler();
|
|
70
|
+
|
|
71
|
+
await app.prepare();
|
|
72
|
+
|
|
73
|
+
createServer((req, res) => {
|
|
74
|
+
const parsedUrl = parse(req.url, true);
|
|
75
|
+
handle(req, res, parsedUrl);
|
|
76
|
+
}).listen(port, () => {
|
|
77
|
+
console.log(`
|
|
78
|
+
Your UI Library is LIVE!
|
|
79
|
+
Open → http://localhost:${port}
|
|
80
|
+
|
|
81
|
+
Click any component → Copy code → Paste into your project!
|
|
82
|
+
|
|
83
|
+
Press Ctrl+C to stop
|
|
84
|
+
`);
|
|
85
|
+
});
|
|
86
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "modula-ui",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A library of modern interfaces with great UX built with Shadcn",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"tailwind",
|
|
7
|
+
"components",
|
|
8
|
+
"copy-paste",
|
|
9
|
+
"ui",
|
|
10
|
+
"nextjs",
|
|
11
|
+
"free"
|
|
12
|
+
],
|
|
13
|
+
"author": "Mary Ojo",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"maryojo": "./bin/run.js",
|
|
18
|
+
"nora-ui": "./bin/run.js"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"src",
|
|
22
|
+
"bin",
|
|
23
|
+
"public",
|
|
24
|
+
"next.config.js",
|
|
25
|
+
"tailwind.config.js",
|
|
26
|
+
"postcss.config.js",
|
|
27
|
+
"components-data.js",
|
|
28
|
+
"registry.json"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"dev": "next dev -p 3177",
|
|
32
|
+
"build": "next build",
|
|
33
|
+
"start": "next start -p 3177"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@radix-ui/react-avatar": "^1.1.11",
|
|
37
|
+
"@radix-ui/react-checkbox": "^1.3.3",
|
|
38
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
39
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
40
|
+
"@radix-ui/react-navigation-menu": "^1.2.14",
|
|
41
|
+
"@radix-ui/react-popover": "^1.1.15",
|
|
42
|
+
"@radix-ui/react-progress": "^1.1.8",
|
|
43
|
+
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
44
|
+
"@radix-ui/react-select": "^2.2.6",
|
|
45
|
+
"@radix-ui/react-separator": "^1.1.8",
|
|
46
|
+
"@radix-ui/react-slider": "^1.3.6",
|
|
47
|
+
"@radix-ui/react-slot": "^1.2.4",
|
|
48
|
+
"@radix-ui/react-tooltip": "^1.2.8",
|
|
49
|
+
"class-variance-authority": "^0.7.1",
|
|
50
|
+
"clsx": "^2.1.1",
|
|
51
|
+
"date-fns": "^4.1.0",
|
|
52
|
+
"framer-motion": "^12.23.24",
|
|
53
|
+
"lucide-react": "^0.553.0",
|
|
54
|
+
"next": "16.0.7",
|
|
55
|
+
"react": "19.2.0",
|
|
56
|
+
"react-day-picker": "^9.11.2",
|
|
57
|
+
"react-dom": "19.2.0",
|
|
58
|
+
"recharts": "^2.15.4",
|
|
59
|
+
"tailwind-merge": "^3.4.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@shadcn/ui": "^0.0.4",
|
|
63
|
+
"@tailwindcss/postcss": "^4",
|
|
64
|
+
"baseline-browser-mapping": "^2.8.30",
|
|
65
|
+
"tailwindcss": "^4",
|
|
66
|
+
"tw-animate-css": "^1.4.0"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=18.17.0"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# 3D Avatar Sources
|
|
2
|
+
|
|
3
|
+
Since the image generation quota was reached, here are some excellent resources for free, commercial-use 3D avatars to complete your collection:
|
|
4
|
+
|
|
5
|
+
## Free 3D Avatar Libraries (PNG/GLTF)
|
|
6
|
+
|
|
7
|
+
1. **IconScout 3D Avatars**
|
|
8
|
+
- link: https://iconscout.com/3d-illustrations/avatar
|
|
9
|
+
- Format: PNG, GLTF, BLEND
|
|
10
|
+
- License: Check individual assets (many are free)
|
|
11
|
+
|
|
12
|
+
2. **Freepik 3D Characters**
|
|
13
|
+
- Link: https://www.freepik.com/search?format=search&query=3d%20avatar%20render%20dark%20background
|
|
14
|
+
- Format: JPG, PSD
|
|
15
|
+
- Note: Excellent for high-quality renders on dark backgrounds.
|
|
16
|
+
|
|
17
|
+
3. **Shapefest**
|
|
18
|
+
- Link: https://www.shapefest.com/
|
|
19
|
+
- Description: A massive library of free 3D shapes and hands, often has character elements.
|
|
20
|
+
|
|
21
|
+
4. **Saly by Alzea**
|
|
22
|
+
- Link: https://www.figma.com/community/file/890095002328610853
|
|
23
|
+
- Description: High-quality 3D illustration pack (Figma resource).
|
|
24
|
+
|
|
25
|
+
5. **Notion Avatar Maker (3D Style)**
|
|
26
|
+
- Link: https://notion-avatar.vercel.app/
|
|
27
|
+
- Note: Mostly 2D but has a distinct style.
|
|
28
|
+
|
|
29
|
+
## Generated Avatars
|
|
30
|
+
The following avatars in this folder were generated by AI:
|
|
31
|
+
- `avatar1.png`: Professional Woman (Dark Background)
|
|
32
|
+
- `avatar2.png`: Casual Man (Dark Background)
|
|
33
|
+
- `avatar3.png`: Duplicate of avatar1 (Placeholder)
|
|
34
|
+
- `avatar4.png`: Duplicate of avatar2 (Placeholder)
|
package/public/file.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
package/public/globe.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
package/public/next.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
package/registry.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"video-conference": {
|
|
3
|
+
"name": "VideoConference",
|
|
4
|
+
"type": "pages",
|
|
5
|
+
"path": "src/library/pages/VideoConference/VideoConferencePage.jsx"
|
|
6
|
+
},
|
|
7
|
+
"group-chat": {
|
|
8
|
+
"name": "GroupChat",
|
|
9
|
+
"type": "pages",
|
|
10
|
+
"path": "src/library/pages/GroupChat/GroupChatPage.jsx"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
|
|
4
|
+
@custom-variant dark (&:is(.dark *));
|
|
5
|
+
|
|
6
|
+
@theme inline {
|
|
7
|
+
--color-background: var(--background);
|
|
8
|
+
--color-foreground: var(--foreground);
|
|
9
|
+
--font-sans: var(--font-lexend);
|
|
10
|
+
--font-mono: var(--font-geist-mono);
|
|
11
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
12
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
13
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
14
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
15
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
16
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
17
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
18
|
+
--color-sidebar: var(--sidebar);
|
|
19
|
+
--color-chart-5: var(--chart-5);
|
|
20
|
+
--color-chart-4: var(--chart-4);
|
|
21
|
+
--color-chart-3: var(--chart-3);
|
|
22
|
+
--color-chart-2: var(--chart-2);
|
|
23
|
+
--color-chart-1: var(--chart-1);
|
|
24
|
+
--color-ring: var(--ring);
|
|
25
|
+
--color-input: var(--input);
|
|
26
|
+
--color-border: var(--border);
|
|
27
|
+
--color-destructive: var(--destructive);
|
|
28
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
29
|
+
--color-accent: var(--accent);
|
|
30
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
31
|
+
--color-muted: var(--muted);
|
|
32
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
33
|
+
--color-secondary: var(--secondary);
|
|
34
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
35
|
+
--color-primary: var(--primary);
|
|
36
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
37
|
+
--color-popover: var(--popover);
|
|
38
|
+
--color-card-foreground: var(--card-foreground);
|
|
39
|
+
--color-card: var(--card);
|
|
40
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
41
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
42
|
+
--radius-lg: var(--radius);
|
|
43
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
:root {
|
|
47
|
+
--radius: 0.625rem;
|
|
48
|
+
--background: oklch(1 0 0);
|
|
49
|
+
--foreground: oklch(0.145 0 0);
|
|
50
|
+
--card: oklch(1 0 0);
|
|
51
|
+
--card-foreground: oklch(0.145 0 0);
|
|
52
|
+
--popover: oklch(1 0 0);
|
|
53
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
54
|
+
--primary: oklch(0.205 0 0);
|
|
55
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
56
|
+
--secondary: oklch(0.97 0 0);
|
|
57
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
58
|
+
--muted: oklch(0.97 0 0);
|
|
59
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
60
|
+
--accent: oklch(0.97 0 0);
|
|
61
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
62
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
63
|
+
--border: oklch(0.922 0 0);
|
|
64
|
+
--input: oklch(0.922 0 0);
|
|
65
|
+
--ring: oklch(0.708 0 0);
|
|
66
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
67
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
68
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
69
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
70
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
71
|
+
--sidebar: oklch(0.985 0 0);
|
|
72
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
73
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
74
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
75
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
76
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
77
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
78
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.dark {
|
|
82
|
+
--background: oklch(0.145 0 0);
|
|
83
|
+
--foreground: oklch(0.985 0 0);
|
|
84
|
+
--card: oklch(0.205 0 0);
|
|
85
|
+
--card-foreground: oklch(0.985 0 0);
|
|
86
|
+
--popover: oklch(0.205 0 0);
|
|
87
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
88
|
+
--primary: oklch(0.922 0 0);
|
|
89
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
90
|
+
--secondary: oklch(0.269 0 0);
|
|
91
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
92
|
+
--muted: oklch(0.269 0 0);
|
|
93
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
94
|
+
--accent: oklch(0.269 0 0);
|
|
95
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
96
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
97
|
+
--border: oklch(1 0 0 / 10%);
|
|
98
|
+
--input: oklch(1 0 0 / 15%);
|
|
99
|
+
--ring: oklch(0.556 0 0);
|
|
100
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
101
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
102
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
103
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
104
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
105
|
+
--sidebar: oklch(0.205 0 0);
|
|
106
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
107
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
108
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
109
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
110
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
111
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
112
|
+
--sidebar-ring: oklch(0.556 0 0);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@layer base {
|
|
116
|
+
* {
|
|
117
|
+
@apply border-border outline-ring/50;
|
|
118
|
+
}
|
|
119
|
+
body {
|
|
120
|
+
@apply bg-background text-foreground;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.lexend {
|
|
124
|
+
font-family: var(--font-lexend), sans-serif;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Lexend, Geist_Mono } from "next/font/google";
|
|
2
|
+
import "./globals.css";
|
|
3
|
+
|
|
4
|
+
const lexend = Lexend({
|
|
5
|
+
variable: "--font-lexend",
|
|
6
|
+
subsets: ["latin"],
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const geistMono = Geist_Mono({
|
|
10
|
+
variable: "--font-geist-mono",
|
|
11
|
+
subsets: ["latin"],
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const metadata = {
|
|
15
|
+
title: "Nora UI",
|
|
16
|
+
description: "A library of modern interfaces with great UX built with Shadcn",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default function RootLayout({ children }) {
|
|
20
|
+
return (
|
|
21
|
+
<html lang="en">
|
|
22
|
+
<body
|
|
23
|
+
className={`${lexend.variable} ${geistMono.variable} antialiased`}
|
|
24
|
+
>
|
|
25
|
+
{children}
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
28
|
+
);
|
|
29
|
+
}
|
package/src/app/page.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import Header from '@/components/Header';
|
|
5
|
+
import MobileOverlay from '@/components/MobileOverlay';
|
|
6
|
+
import Sidebar from '@/components/Sidebar';
|
|
7
|
+
import PreviewCard from '@/components/PreviewCard';
|
|
8
|
+
import CodeCard from '@/components/CodeCard';
|
|
9
|
+
import { components } from '@/data/componentData';
|
|
10
|
+
|
|
11
|
+
export default function Home() {
|
|
12
|
+
const [selected, setSelected] = useState('VideoConference');
|
|
13
|
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
14
|
+
|
|
15
|
+
const current = components[selected];
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100">
|
|
19
|
+
|
|
20
|
+
<Header
|
|
21
|
+
sidebarOpen={sidebarOpen}
|
|
22
|
+
toggleSidebar={() => setSidebarOpen(!sidebarOpen)}
|
|
23
|
+
/>
|
|
24
|
+
|
|
25
|
+
<MobileOverlay open={sidebarOpen} onClose={() => setSidebarOpen(false)} />
|
|
26
|
+
|
|
27
|
+
<div className="w-full mx-auto px-6 flex gap-6">
|
|
28
|
+
|
|
29
|
+
<Sidebar
|
|
30
|
+
selected={selected}
|
|
31
|
+
onSelect={setSelected}
|
|
32
|
+
open={sidebarOpen}
|
|
33
|
+
onClose={() => setSidebarOpen(false)}
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
<main className="space-y-6 w-full">
|
|
37
|
+
<PreviewCard>{current?.preview}</PreviewCard>
|
|
38
|
+
|
|
39
|
+
<CodeCard code={current?.code} />
|
|
40
|
+
|
|
41
|
+
{/* add use case and tags to this */}
|
|
42
|
+
|
|
43
|
+
<div className='m-10'>
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
</main>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import Header from '@/components/Header';
|
|
5
|
+
import MobileOverlay from '@/components/MobileOverlay';
|
|
6
|
+
import Sidebar from '@/components/Sidebar';
|
|
7
|
+
import PreviewCard from '@/components/PreviewCard';
|
|
8
|
+
import CodeCard from '@/components/CodeCard';
|
|
9
|
+
import { components } from '@/data/componentData';
|
|
10
|
+
|
|
11
|
+
export default function Patterns() {
|
|
12
|
+
const [selected, setSelected] = useState('button');
|
|
13
|
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
14
|
+
|
|
15
|
+
const current = components[selected];
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100">
|
|
19
|
+
|
|
20
|
+
<Header
|
|
21
|
+
sidebarOpen={sidebarOpen}
|
|
22
|
+
toggleSidebar={() => setSidebarOpen(!sidebarOpen)}
|
|
23
|
+
/>
|
|
24
|
+
|
|
25
|
+
<MobileOverlay open={sidebarOpen} onClose={() => setSidebarOpen(false)} />
|
|
26
|
+
|
|
27
|
+
<div className="w-full mx-auto px-6 flex gap-6">
|
|
28
|
+
|
|
29
|
+
{sidebarOpen && <Sidebar
|
|
30
|
+
selected={selected}
|
|
31
|
+
onSelect={setSelected}
|
|
32
|
+
open={sidebarOpen}
|
|
33
|
+
onClose={() => setSidebarOpen(false)}
|
|
34
|
+
/>}
|
|
35
|
+
|
|
36
|
+
<main className="space-y-6 w-full">
|
|
37
|
+
<PreviewCard>{current?.preview}</PreviewCard>
|
|
38
|
+
|
|
39
|
+
<CodeCard code={current?.code} />
|
|
40
|
+
|
|
41
|
+
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
|
42
|
+
<p className="text-sm text-blue-800">
|
|
43
|
+
<strong>Note:</strong> These components use Tailwind CSS utility classes. Make sure you have Tailwind CSS installed in your project.
|
|
44
|
+
</p>
|
|
45
|
+
</div>
|
|
46
|
+
</main>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import CopyButton from './CopyButton';
|
|
2
|
+
|
|
3
|
+
export default function CodeCard({ code }) {
|
|
4
|
+
return (
|
|
5
|
+
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
|
6
|
+
<div className="flex items-center justify-between mb-4">
|
|
7
|
+
<h2 className="text-xl font-semibold text-gray-900">Code</h2>
|
|
8
|
+
<CopyButton text={code} />
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<pre className="bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto text-xs md:text-sm">
|
|
12
|
+
<code>{code?.trim()}</code>
|
|
13
|
+
</pre>
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { Copy, Check } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
export default function CopyButton({ text }) {
|
|
7
|
+
const [copied, setCopied] = useState(false);
|
|
8
|
+
|
|
9
|
+
const copy = () => {
|
|
10
|
+
navigator.clipboard.writeText(text);
|
|
11
|
+
setCopied(true);
|
|
12
|
+
setTimeout(() => setCopied(false), 2000);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<button
|
|
17
|
+
onClick={copy}
|
|
18
|
+
className="flex items-center gap-2 px-4 py-2 bg-gray-900 text-white rounded-lg hover:bg-gray-800 transition-colors text-sm"
|
|
19
|
+
>
|
|
20
|
+
{copied ? (
|
|
21
|
+
<>
|
|
22
|
+
<Check size={18} /> Copied!
|
|
23
|
+
</>
|
|
24
|
+
) : (
|
|
25
|
+
<>
|
|
26
|
+
<Copy size={18} /> Copy Code
|
|
27
|
+
</>
|
|
28
|
+
)}
|
|
29
|
+
</button>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Menu, X } from 'lucide-react';
|
|
4
|
+
import Logo from './Logo';
|
|
5
|
+
|
|
6
|
+
export default function Header({ sidebarOpen, toggleSidebar }) {
|
|
7
|
+
return (
|
|
8
|
+
<div className="max-w-7xl mx-auto p-6">
|
|
9
|
+
<div className="mb-8 flex items-center justify-between">
|
|
10
|
+
<div>
|
|
11
|
+
<Logo />
|
|
12
|
+
<p className="text-gray-600">A library of modern interfaces with great UX built with Shadcn</p>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<button
|
|
16
|
+
onClick={toggleSidebar}
|
|
17
|
+
className="p-2 bg-white rounded-lg shadow-sm border border-gray-200 hover:bg-gray-50"
|
|
18
|
+
>
|
|
19
|
+
{sidebarOpen ? <X size={24} /> : <Menu size={24} />}
|
|
20
|
+
</button>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
const Logo = ({ className = '' }) => {
|
|
4
|
+
return (
|
|
5
|
+
<svg
|
|
6
|
+
width="180"
|
|
7
|
+
height="50"
|
|
8
|
+
viewBox="0 0 180 50"
|
|
9
|
+
fill="none"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
className={className}
|
|
12
|
+
>
|
|
13
|
+
{/* Icon Element - Abstract 'N' or Tech shape */}
|
|
14
|
+
<circle cx="25" cy="25" r="20" fill="url(#grad1)" opacity="0.2" />
|
|
15
|
+
<path
|
|
16
|
+
d="M15 35V15L35 35V15"
|
|
17
|
+
stroke="url(#grad2)"
|
|
18
|
+
strokeWidth="4"
|
|
19
|
+
strokeLinecap="round"
|
|
20
|
+
strokeLinejoin="round"
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
{/* Text 'nora' */}
|
|
24
|
+
<text
|
|
25
|
+
x="55"
|
|
26
|
+
y="32"
|
|
27
|
+
fontFamily="system-ui, -apple-system, sans-serif"
|
|
28
|
+
fontWeight="800"
|
|
29
|
+
fontSize="28"
|
|
30
|
+
fill="#111827"
|
|
31
|
+
letterSpacing="-1"
|
|
32
|
+
>
|
|
33
|
+
nora
|
|
34
|
+
</text>
|
|
35
|
+
|
|
36
|
+
{/* Text '-ui' with different style */}
|
|
37
|
+
<text
|
|
38
|
+
x="115"
|
|
39
|
+
y="32"
|
|
40
|
+
fontFamily="system-ui, -apple-system, sans-serif"
|
|
41
|
+
fontWeight="800"
|
|
42
|
+
fontSize="28"
|
|
43
|
+
fill="#111827"
|
|
44
|
+
letterSpacing="-1"
|
|
45
|
+
>
|
|
46
|
+
ui
|
|
47
|
+
</text>
|
|
48
|
+
|
|
49
|
+
{/* Gradients */}
|
|
50
|
+
<defs>
|
|
51
|
+
<radialGradient id="grad1" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(25 25) rotate(90) scale(20)">
|
|
52
|
+
<stop stopColor="#4F46E5" />
|
|
53
|
+
<stop offset="1" stopColor="#A5B4FC" stopOpacity="0" />
|
|
54
|
+
</radialGradient>
|
|
55
|
+
<linearGradient id="grad2" x1="15" y1="15" x2="35" y2="35" gradientUnits="userSpaceOnUse">
|
|
56
|
+
<stop stopColor="#4F46E5" />
|
|
57
|
+
<stop offset="1" stopColor="#EC4899" />
|
|
58
|
+
</linearGradient>
|
|
59
|
+
</defs>
|
|
60
|
+
</svg>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export default Logo;
|