modula-ui 1.0.2 → 1.0.4
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 +84 -21
- package/bin/run.js +23 -21
- package/package.json +8 -5
- package/public/google.svg +2 -0
- package/public/logo_cloudsync.png +0 -0
- package/registry.json +11 -13
- package/src/app/globals.css +40 -1
- package/src/app/group-chat/page.jsx +6 -0
- package/src/app/layout.js +10 -5
- package/src/app/login-demo/page.jsx +5 -0
- package/src/app/page.js +1 -1
- package/src/app/video-conference/page.jsx +6 -0
- package/src/components/Header.jsx +1 -1
- package/src/components/Logo.jsx +1 -1
- package/src/components/PreviewCard.jsx +6 -69
- package/src/components/Sidebar.jsx +1 -1
- package/src/components/login-template/google.svg +2 -0
- package/src/components/login-template/index.css +4 -0
- package/src/components/login-template/index.jsx +9 -0
- package/src/components/login-template/page.jsx +99 -0
- package/src/components/login-template/particles-background.jsx +100 -0
- package/src/components/ui/label.jsx +24 -0
- package/src/data/componentData.js +3 -2
- package/src/library/pages/GroupChat/index.jsx +2 -1
- package/src/library/pages/VideoConference/index.jsx +2 -1
package/README.md
CHANGED
|
@@ -1,35 +1,98 @@
|
|
|
1
|
-
|
|
1
|
+
# Modula UI
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
A library of modern UI patterns and complete interfaces built with [Shadcn](https://ui.shadcn.com/) and [Tailwind CSS](https://tailwindcss.com/). Get beautiful, functional UIs without building from scratch.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
- 🎨 **Complete UI Patterns** - Full-featured interfaces, not just individual components
|
|
8
|
+
- 🚀 **Easy Integration** - Simple CLI to add entire UI blocks to your project
|
|
9
|
+
- 📦 **Copy & Paste** - Browse interfaces in your browser and copy code directly
|
|
10
|
+
- 🎯 **Modern Stack** - Built with Next.js, React 19, and Tailwind CSS 4
|
|
11
|
+
- 🔧 **Customizable** - Full control over styling and behavior
|
|
12
|
+
- 📱 **Responsive** - Mobile-first design approach
|
|
13
|
+
|
|
14
|
+
## 🚀 Quick Start
|
|
15
|
+
|
|
16
|
+
### Browse UI Patterns
|
|
17
|
+
|
|
18
|
+
Run the library locally to browse all available UI patterns and interfaces:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx modula-ui
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This will start a local server at `http://localhost:3177` where you can:
|
|
25
|
+
- Preview all UI patterns
|
|
26
|
+
- Copy interface code
|
|
27
|
+
- See live examples
|
|
28
|
+
|
|
29
|
+
### Add UI Patterns to Your Project
|
|
30
|
+
|
|
31
|
+
Use the CLI to add complete interfaces directly to your project:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx modula-ui add <component-name>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The CLI will automatically:
|
|
38
|
+
1. Install required dependencies
|
|
39
|
+
2. Install necessary Shadcn UI components
|
|
40
|
+
3. Copy the complete interface files to your project
|
|
41
|
+
|
|
42
|
+
## 📦 Available UI Patterns
|
|
43
|
+
|
|
44
|
+
- **video-conference** - Full-featured video conferencing interface with participant grid, controls, and animations
|
|
45
|
+
- **group-chat** - Modern group chat interface with sidebar, message threads, and real-time feel
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## 🛠️ Installation
|
|
49
|
+
|
|
50
|
+
### Prerequisites
|
|
51
|
+
|
|
52
|
+
- Node.js >= 18.17.0
|
|
53
|
+
- A Next.js or React project with Tailwind CSS configured
|
|
54
|
+
|
|
55
|
+
### Example: Adding a UI Block
|
|
5
56
|
|
|
6
57
|
```bash
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
# or
|
|
13
|
-
bun dev
|
|
58
|
+
# Add the video conference page
|
|
59
|
+
npx modula-ui add video-conference
|
|
60
|
+
|
|
61
|
+
# Add the group chat interface
|
|
62
|
+
npx modula-ui add group-chat
|
|
14
63
|
```
|
|
15
64
|
|
|
16
|
-
|
|
65
|
+
The interface will be added to `src/library/<type>/` (or `library/<type>/` if no `src` folder exists).
|
|
17
66
|
|
|
18
|
-
|
|
67
|
+
## 🎯 Usage
|
|
19
68
|
|
|
20
|
-
|
|
69
|
+
After adding a UI pattern, import and use it in your project:
|
|
70
|
+
|
|
71
|
+
```jsx
|
|
72
|
+
import VideoConferencePage from '@/library/pages/VideoConferencePage';
|
|
73
|
+
|
|
74
|
+
export default function MyPage() {
|
|
75
|
+
return <VideoConferencePage />;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
21
78
|
|
|
22
|
-
##
|
|
79
|
+
## 🏗️ Tech Stack
|
|
23
80
|
|
|
24
|
-
|
|
81
|
+
- **Framework:** Next.js 16
|
|
82
|
+
- **UI Library:** React 19
|
|
83
|
+
- **Styling:** Tailwind CSS 4
|
|
84
|
+
- **Components:** Radix UI primitives
|
|
85
|
+
- **Animations:** Framer Motion
|
|
86
|
+
- **Icons:** Lucide React
|
|
87
|
+
- **Charts:** Recharts
|
|
25
88
|
|
|
26
|
-
|
|
27
|
-
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
89
|
+
## 📄 License
|
|
28
90
|
|
|
29
|
-
|
|
91
|
+
MIT © Mary Ojo
|
|
30
92
|
|
|
31
|
-
## Deploy on Vercel
|
|
32
93
|
|
|
33
|
-
|
|
94
|
+
## 🔗 Links
|
|
34
95
|
|
|
35
|
-
|
|
96
|
+
- [npm Package](https://www.npmjs.com/package/modula-ui)
|
|
97
|
+
- [Shadcn UI](https://ui.shadcn.com/)
|
|
98
|
+
- [Tailwind CSS](https://tailwindcss.com/)
|
package/bin/run.js
CHANGED
|
@@ -125,26 +125,28 @@ if (command === 'add') {
|
|
|
125
125
|
process.exit(1);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// else {
|
|
131
|
+
// // Default behavior: Start the Next.js server
|
|
132
|
+
// const port = 3177;
|
|
133
|
+
// const dev = false;
|
|
134
|
+
// const app = next({ dev, dir: packageRoot });
|
|
135
|
+
// const handle = app.getRequestHandler();
|
|
136
|
+
|
|
137
|
+
// await app.prepare();
|
|
138
|
+
|
|
139
|
+
// createServer((req, res) => {
|
|
140
|
+
// const parsedUrl = parse(req.url, true);
|
|
141
|
+
// handle(req, res, parsedUrl);
|
|
142
|
+
// }).listen(port, () => {
|
|
143
|
+
// console.log(`
|
|
144
|
+
// Your UI Library is LIVE!
|
|
145
|
+
// Open → http://localhost:${port}
|
|
144
146
|
|
|
145
|
-
|
|
147
|
+
// Click any component → Copy code → Paste into your project!
|
|
146
148
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
149
|
+
// Press Ctrl+C to stop
|
|
150
|
+
// `);
|
|
151
|
+
// });
|
|
152
|
+
// }
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modula-ui",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "A library of modern
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "A library of beautiful modern UI blocks and patterns built with Shadcn by Mary Ojo",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tailwind",
|
|
7
7
|
"components",
|
|
@@ -28,15 +28,16 @@
|
|
|
28
28
|
"registry.json"
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
|
-
"dev": "next dev
|
|
31
|
+
"dev": "next dev",
|
|
32
32
|
"build": "next build",
|
|
33
|
-
"start": "next start
|
|
33
|
+
"start": "next start"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@radix-ui/react-avatar": "^1.1.11",
|
|
37
37
|
"@radix-ui/react-checkbox": "^1.3.3",
|
|
38
38
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
39
39
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
40
|
+
"@radix-ui/react-label": "^2.1.8",
|
|
40
41
|
"@radix-ui/react-navigation-menu": "^1.2.14",
|
|
41
42
|
"@radix-ui/react-popover": "^1.1.15",
|
|
42
43
|
"@radix-ui/react-progress": "^1.1.8",
|
|
@@ -46,12 +47,14 @@
|
|
|
46
47
|
"@radix-ui/react-slider": "^1.3.6",
|
|
47
48
|
"@radix-ui/react-slot": "^1.2.4",
|
|
48
49
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
50
|
+
"@tsparticles/react": "^3.0.0",
|
|
51
|
+
"@tsparticles/slim": "^3.9.1",
|
|
49
52
|
"class-variance-authority": "^0.7.1",
|
|
50
53
|
"clsx": "^2.1.1",
|
|
51
54
|
"date-fns": "^4.1.0",
|
|
52
55
|
"framer-motion": "^12.23.24",
|
|
53
56
|
"lucide-react": "^0.553.0",
|
|
54
|
-
"next": "16.0.
|
|
57
|
+
"next": "16.0.10",
|
|
55
58
|
"react": "19.2.0",
|
|
56
59
|
"react-day-picker": "^9.11.2",
|
|
57
60
|
"react-dom": "19.2.0",
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
2
|
+
<svg width="800px" height="800px" viewBox="-3 0 262 262" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622 38.755 30.023 2.685.268c24.659-22.774 38.875-56.282 38.875-96.027" fill="#4285F4"/><path d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055-34.523 0-63.824-22.773-74.269-54.25l-1.531.13-40.298 31.187-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1" fill="#34A853"/><path d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82 0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602l42.356-32.782" fill="#FBBC05"/><path d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0 79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251" fill="#EB4335"/></svg>
|
|
Binary file
|
package/registry.json
CHANGED
|
@@ -13,25 +13,23 @@
|
|
|
13
13
|
"avatar"
|
|
14
14
|
]
|
|
15
15
|
},
|
|
16
|
-
"
|
|
17
|
-
"name": "
|
|
18
|
-
"type": "
|
|
19
|
-
"path": "src/
|
|
16
|
+
"login-page-01": {
|
|
17
|
+
"name": "Login Page 01",
|
|
18
|
+
"type": "blocks",
|
|
19
|
+
"path": "src/components/login-template/page.jsx",
|
|
20
20
|
"files": [
|
|
21
|
-
"src/
|
|
22
|
-
"src/
|
|
21
|
+
"src/components/login-template/page.jsx",
|
|
22
|
+
"src/components/login-template/particles-background.jsx"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": [
|
|
25
|
-
"
|
|
25
|
+
"@tsparticles/react",
|
|
26
|
+
"@tsparticles/slim"
|
|
26
27
|
],
|
|
27
28
|
"registryDependencies": [
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"badge",
|
|
31
|
-
"scroll-area",
|
|
32
|
-
"navigation-menu",
|
|
29
|
+
"button",
|
|
30
|
+
"card",
|
|
33
31
|
"input",
|
|
34
|
-
"
|
|
32
|
+
"label"
|
|
35
33
|
]
|
|
36
34
|
}
|
|
37
35
|
}
|
package/src/app/globals.css
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
@import "tw-animate-css";
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
@custom-variant dark (&:is(.dark *));
|
|
5
6
|
|
|
6
7
|
@theme inline {
|
|
7
8
|
--color-background: var(--background);
|
|
8
9
|
--color-foreground: var(--foreground);
|
|
9
|
-
--font-sans: var(--font-
|
|
10
|
+
--font-sans: var(--font-dm-sans);
|
|
10
11
|
--font-mono: var(--font-geist-mono);
|
|
11
12
|
--color-sidebar-ring: var(--sidebar-ring);
|
|
12
13
|
--color-sidebar-border: var(--sidebar-border);
|
|
@@ -124,3 +125,41 @@
|
|
|
124
125
|
font-family: var(--font-lexend), sans-serif;
|
|
125
126
|
}
|
|
126
127
|
}
|
|
128
|
+
|
|
129
|
+
@layer components {
|
|
130
|
+
.glass {
|
|
131
|
+
/* Semi-transparent background – usually white or light tint */
|
|
132
|
+
background: rgba(255, 255, 255, 0.15); /* 0.1 – 0.25 is most common */
|
|
133
|
+
|
|
134
|
+
/* The magic line – blurs what's behind */
|
|
135
|
+
backdrop-filter: blur(12px);
|
|
136
|
+
-webkit-backdrop-filter: blur(12px); /* Safari support */
|
|
137
|
+
|
|
138
|
+
/* Softer rounded corners */
|
|
139
|
+
border-radius: 16px;
|
|
140
|
+
|
|
141
|
+
/* Subtle border that looks like glass edge */
|
|
142
|
+
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
143
|
+
|
|
144
|
+
/* Optional – adds depth & modern feel */
|
|
145
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
|
|
146
|
+
|
|
147
|
+
/* Important for readability */
|
|
148
|
+
color: white;
|
|
149
|
+
padding: 2rem;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.glass-dark {
|
|
153
|
+
background: rgba(31, 31, 32, 0.75); /* dark tint */
|
|
154
|
+
backdrop-filter: blur(25px);
|
|
155
|
+
-webkit-backdrop-filter: blur(25px);
|
|
156
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
157
|
+
border-radius: 20px;
|
|
158
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
|
|
159
|
+
color: #f0f0ff;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
#tsparticles, #tsparticles canvas {
|
|
163
|
+
pointer-events: none !important;
|
|
164
|
+
}
|
|
165
|
+
}
|
package/src/app/layout.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DM_Sans, Geist, Geist_Mono } from "next/font/google";
|
|
2
2
|
import "./globals.css";
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
variable: "--font-
|
|
4
|
+
const dmSans = DM_Sans({
|
|
5
|
+
variable: "--font-dm-sans",
|
|
6
|
+
subsets: ["latin"],
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const geistSans = Geist({
|
|
10
|
+
variable: "--font-geist-sans",
|
|
6
11
|
subsets: ["latin"],
|
|
7
12
|
});
|
|
8
13
|
|
|
@@ -13,14 +18,14 @@ const geistMono = Geist_Mono({
|
|
|
13
18
|
|
|
14
19
|
export const metadata = {
|
|
15
20
|
title: "Modula UI",
|
|
16
|
-
description: "A library of modern
|
|
21
|
+
description: "A library of beautiful modern UI blocks and patterns built with care and Shadcn by Mary Ojo",
|
|
17
22
|
};
|
|
18
23
|
|
|
19
24
|
export default function RootLayout({ children }) {
|
|
20
25
|
return (
|
|
21
26
|
<html lang="en">
|
|
22
27
|
<body
|
|
23
|
-
className={`${
|
|
28
|
+
className={`${dmSans.variable} ${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
24
29
|
>
|
|
25
30
|
{children}
|
|
26
31
|
</body>
|
package/src/app/page.js
CHANGED
|
@@ -9,7 +9,7 @@ export default function Header({ sidebarOpen, toggleSidebar }) {
|
|
|
9
9
|
<div className="mb-8 flex items-center justify-between">
|
|
10
10
|
<div>
|
|
11
11
|
<Logo />
|
|
12
|
-
<p className="text-gray-600">A library of modern
|
|
12
|
+
<p className="text-gray-600">A library of beautiful modern UI blocks and patterns built with care and Shadcn by <a href="https://www.linkedin.com/in/mary-ojo/" className='underline'>Mary (a Design Engineer)</a></p>
|
|
13
13
|
</div>
|
|
14
14
|
|
|
15
15
|
<button
|
package/src/components/Logo.jsx
CHANGED
|
@@ -1,80 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
import { createPortal } from "react-dom";
|
|
3
|
-
|
|
4
|
-
export default function PreviewCard({ children, title = "Preview" }) {
|
|
5
|
-
const [externalWindow, setExternalWindow] = useState(null);
|
|
6
|
-
const [containerElement, setContainerElement] = useState(null);
|
|
7
|
-
|
|
1
|
+
export default function PreviewCard({ children, title = "Preview", url }) {
|
|
8
2
|
const openFullPreview = () => {
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
return;
|
|
3
|
+
if (url) {
|
|
4
|
+
window.open(url, "_blank");
|
|
12
5
|
}
|
|
13
|
-
|
|
14
|
-
const newWindow = window.open("", "_blank", "width=1200,height=800,left=200,top=200");
|
|
15
|
-
if (!newWindow) return;
|
|
16
|
-
|
|
17
|
-
const fullPage = `
|
|
18
|
-
<!DOCTYPE html>
|
|
19
|
-
<html lang="en">
|
|
20
|
-
<head>
|
|
21
|
-
<meta charset="utf-8">
|
|
22
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
23
|
-
<title>${title}</title>
|
|
24
|
-
<script src="https://cdn.tailwindcss.com"></script>
|
|
25
|
-
<style>
|
|
26
|
-
body { margin: 0; background: #f9fafb; }
|
|
27
|
-
@media (prefers-color-scheme: dark) { body { background: #111827; } }
|
|
28
|
-
</style>
|
|
29
|
-
</head>
|
|
30
|
-
<body class="min-h-screen flex items-center justify-center">
|
|
31
|
-
<div id="root" class="w-full mx-auto"></div>
|
|
32
|
-
</body>
|
|
33
|
-
</html>
|
|
34
|
-
`;
|
|
35
|
-
|
|
36
|
-
newWindow.document.write(fullPage);
|
|
37
|
-
newWindow.document.close();
|
|
38
|
-
|
|
39
|
-
// Wait for the window to load before trying to access the DOM
|
|
40
|
-
newWindow.onload = () => {
|
|
41
|
-
const container = newWindow.document.getElementById("root");
|
|
42
|
-
setExternalWindow(newWindow);
|
|
43
|
-
setContainerElement(container);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// In case onload fired synchronously or we missed it (rare with open, but good safety)
|
|
47
|
-
if (newWindow.document.readyState === 'complete') {
|
|
48
|
-
const container = newWindow.document.getElementById("root");
|
|
49
|
-
setExternalWindow(newWindow);
|
|
50
|
-
setContainerElement(container);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Handle window close
|
|
54
|
-
newWindow.onbeforeunload = () => {
|
|
55
|
-
setExternalWindow(null);
|
|
56
|
-
setContainerElement(null);
|
|
57
|
-
};
|
|
58
6
|
};
|
|
59
7
|
|
|
60
|
-
// Close external window when component unmounts
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
return () => {
|
|
63
|
-
if (externalWindow) {
|
|
64
|
-
externalWindow.close();
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}, [externalWindow]);
|
|
68
|
-
|
|
69
8
|
return (
|
|
70
9
|
<div className="w-full bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
|
71
10
|
<div className="flex items-center justify-between mb-4">
|
|
72
11
|
<h2 className="text-xl font-semibold text-gray-900">{title}</h2>
|
|
73
12
|
|
|
74
13
|
<button
|
|
14
|
+
type="button"
|
|
75
15
|
onClick={openFullPreview}
|
|
76
16
|
className={`flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-md transition
|
|
77
|
-
text-gray-700 bg-gray-50 hover:bg-gray-100 hover:text-gray-900`}
|
|
17
|
+
text-gray-700 bg-gray-50 hover:bg-gray-100 hover:text-gray-900 cursor-pointer`}
|
|
78
18
|
>
|
|
79
19
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
80
20
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
|
@@ -86,13 +26,10 @@ export default function PreviewCard({ children, title = "Preview" }) {
|
|
|
86
26
|
{/* The preview area in the main window */}
|
|
87
27
|
<div
|
|
88
28
|
id="preview-content"
|
|
89
|
-
className="bg-gray-50 rounded-lg p-4 border border-dashed border-gray-300 min-h-96 w-full"
|
|
29
|
+
className="bg-gray-50 rounded-lg p-4 border border-dashed border-gray-300 min-h-96 max-h-[600px] w-full overflow-hidden"
|
|
90
30
|
>
|
|
91
31
|
{children}
|
|
92
32
|
</div>
|
|
93
|
-
|
|
94
|
-
{/* Render content into the new window if it exists */}
|
|
95
|
-
{containerElement && createPortal(children, containerElement)}
|
|
96
33
|
</div>
|
|
97
34
|
);
|
|
98
35
|
}
|
|
@@ -15,7 +15,7 @@ export default function Sidebar({ selected, onSelect, open, onClose }) {
|
|
|
15
15
|
style={{ top: '1.5rem' }} // matches Header padding
|
|
16
16
|
>
|
|
17
17
|
<div className="flex items-center justify-between mb-4 lg:mb-3">
|
|
18
|
-
<h2 className="font-semibold text-gray-700">
|
|
18
|
+
<h2 className="font-semibold text-gray-700">Blocks</h2>
|
|
19
19
|
<button
|
|
20
20
|
onClick={onClose}
|
|
21
21
|
className="lg:hidden p-1 hover:bg-gray-100 rounded"
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
2
|
+
<svg width="800px" height="800px" viewBox="-3 0 262 262" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622 38.755 30.023 2.685.268c24.659-22.774 38.875-56.282 38.875-96.027" fill="#4285F4"/><path d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055-34.523 0-63.824-22.773-74.269-54.25l-1.531.13-40.298 31.187-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1" fill="#34A853"/><path d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82 0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602l42.356-32.782" fill="#FBBC05"/><path d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0 79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251" fill="#EB4335"/></svg>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Button } from "@/components/ui/button"
|
|
4
|
+
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
|
|
5
|
+
import { Input } from "@/components/ui/input"
|
|
6
|
+
import { Label } from "@/components/ui/label"
|
|
7
|
+
import { ParticlesBackground } from "./particles-background"
|
|
8
|
+
import logo from "../../../public/logo_cloudsync.png"
|
|
9
|
+
import google from "../../../public/google.svg"
|
|
10
|
+
import Image from "next/image"
|
|
11
|
+
|
|
12
|
+
export default function LoginPage() {
|
|
13
|
+
return (
|
|
14
|
+
<div className="dark font-sans w-full min-h-screen flex flex-col lg:flex-row items-center justify-center lg:justify-end overflow-hidden bg-background text-foreground">
|
|
15
|
+
<ParticlesBackground />
|
|
16
|
+
|
|
17
|
+
<div className="w-full z-10 px-10 sm:px-6 lg:px-20 py-12 md:py-12 flex flex-col lg:flex-row items-center justify-center gap-8 lg:gap-28">
|
|
18
|
+
<div className="hidden lg:block space-y-4 w-full lg:w-1/2">
|
|
19
|
+
<div className="flex items-center gap-4 mb-4">
|
|
20
|
+
<Image
|
|
21
|
+
src={logo}
|
|
22
|
+
alt="Logo"
|
|
23
|
+
width={65}
|
|
24
|
+
height={65}
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
27
|
+
<h3 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold tracking-tight text-white">Secure, Scalable and Seamless</h3>
|
|
28
|
+
<p className="text-sm sm:text-base md:text-lg text-muted-foreground">Empowering businesses with world-class cloud computing infrastructure and 99.9% uptime. Managed, monitored and optimized for your growth.</p>
|
|
29
|
+
</div>
|
|
30
|
+
<div className="lg:hidden flex justify-center mb-4">
|
|
31
|
+
<Image
|
|
32
|
+
src={logo}
|
|
33
|
+
alt="Logo"
|
|
34
|
+
width={65}
|
|
35
|
+
height={65}
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
<Card className="w-full md:w-[70%] lg:w-1/2 glass-dark">
|
|
39
|
+
<CardHeader className="space-y-1 ">
|
|
40
|
+
<CardTitle className="text-xl sm:text-2xl font-bold tracking-tight">Welcome back</CardTitle>
|
|
41
|
+
<CardDescription className="text-sm sm:text-base text-muted-foreground/80">
|
|
42
|
+
Enter your email to sign in to your account
|
|
43
|
+
</CardDescription>
|
|
44
|
+
</CardHeader>
|
|
45
|
+
<div className="relative my-1">
|
|
46
|
+
<div className="absolute inset-0 flex items-center">
|
|
47
|
+
<span className="w-full border-t border-muted-foreground/20" />
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
<CardContent className="space-y-4">
|
|
51
|
+
<div className="space-y-2">
|
|
52
|
+
<Label htmlFor="email" className="text-sm">Email</Label>
|
|
53
|
+
<Input id="email" placeholder="m@example.com" required type="email" className="bg-white/5 backdrop-blur-sm border-white/10 focus:bg-white/10 focus:border-white/20 transition-all text-sm" />
|
|
54
|
+
</div>
|
|
55
|
+
<div className="space-y-2">
|
|
56
|
+
<Label htmlFor="password" className="text-sm">Password</Label>
|
|
57
|
+
<Input id="password" required placeholder="********" type="password" className="bg-white/5 backdrop-blur-sm border-white/10 focus:bg-white/10 focus:border-white/20 transition-all text-sm" />
|
|
58
|
+
</div>
|
|
59
|
+
<Button className="w-full bg-cyan-500 shadow-lg shadow-primary/20 hover:shadow-primary/40 transition-shadow text-sm sm:text-base" type="submit">
|
|
60
|
+
Sign In
|
|
61
|
+
</Button>
|
|
62
|
+
|
|
63
|
+
<div className="relative my-2 mb-3">
|
|
64
|
+
<div className="absolute inset-0 flex items-center">
|
|
65
|
+
<span className="w-full border-t border-muted-foreground/20" />
|
|
66
|
+
</div>
|
|
67
|
+
<div className="relative flex justify-center text-xs uppercase">
|
|
68
|
+
<span className="bg-transparent px-2 text-muted-foreground">
|
|
69
|
+
Or continue with
|
|
70
|
+
</span>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<Button variant="outline" className="w-full bg-white/5 backdrop-blur-sm border-white/10 hover:bg-white/10 hover:text-white text-sm sm:text-base" onClick={() => { }}>
|
|
75
|
+
<Image
|
|
76
|
+
src={google}
|
|
77
|
+
alt="Google"
|
|
78
|
+
width={16}
|
|
79
|
+
height={16}
|
|
80
|
+
/>
|
|
81
|
+
Google
|
|
82
|
+
</Button>
|
|
83
|
+
</CardContent>
|
|
84
|
+
<CardFooter className="flex flex-col gap-2 text-center text-xs sm:text-sm text-muted-foreground">
|
|
85
|
+
<a href="#" className="hover:text-primary transition-colors hover:underline">
|
|
86
|
+
Forgot your password?
|
|
87
|
+
</a>
|
|
88
|
+
<div className="text-xs">
|
|
89
|
+
Don't have an account?{" "}
|
|
90
|
+
<a href="#" className="font-medium text-primary hover:underline transition-colors">
|
|
91
|
+
Sign up for free
|
|
92
|
+
</a>
|
|
93
|
+
</div>
|
|
94
|
+
</CardFooter>
|
|
95
|
+
</Card>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react"
|
|
4
|
+
import Particles, { initParticlesEngine } from "@tsparticles/react"
|
|
5
|
+
import { loadSlim } from "@tsparticles/slim"
|
|
6
|
+
|
|
7
|
+
export function ParticlesBackground() {
|
|
8
|
+
const [init, setInit] = useState(false)
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
initParticlesEngine(async (engine) => {
|
|
12
|
+
await loadSlim(engine)
|
|
13
|
+
}).then(() => {
|
|
14
|
+
setInit(true)
|
|
15
|
+
})
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
if (!init) {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const options = {
|
|
23
|
+
particles: {
|
|
24
|
+
number: {
|
|
25
|
+
value: 30,
|
|
26
|
+
density: {
|
|
27
|
+
enable: true,
|
|
28
|
+
width: 800,
|
|
29
|
+
height: 800
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
color: {
|
|
33
|
+
value: "#FFFEE0"
|
|
34
|
+
},
|
|
35
|
+
shape: {
|
|
36
|
+
type: "circle",
|
|
37
|
+
},
|
|
38
|
+
opacity: {
|
|
39
|
+
value: 0.5,
|
|
40
|
+
animation: {
|
|
41
|
+
enable: false
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
size: {
|
|
45
|
+
value: { min: 1, max: 3 },
|
|
46
|
+
},
|
|
47
|
+
links: {
|
|
48
|
+
enable: true,
|
|
49
|
+
distance: 150,
|
|
50
|
+
color: "#00FFFF",
|
|
51
|
+
opacity: 0.2,
|
|
52
|
+
width: 1
|
|
53
|
+
},
|
|
54
|
+
move: {
|
|
55
|
+
enable: true,
|
|
56
|
+
speed: 0.1,
|
|
57
|
+
direction: "none",
|
|
58
|
+
random: false,
|
|
59
|
+
straight: false,
|
|
60
|
+
outModes: {
|
|
61
|
+
default: "out"
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
interactivity: {
|
|
66
|
+
detectsOn: "canvas",
|
|
67
|
+
events: {
|
|
68
|
+
onHover: {
|
|
69
|
+
enable: false,
|
|
70
|
+
mode: "repulse"
|
|
71
|
+
},
|
|
72
|
+
onClick: {
|
|
73
|
+
enable: false,
|
|
74
|
+
mode: "push"
|
|
75
|
+
},
|
|
76
|
+
resize: {
|
|
77
|
+
enable: false
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
modes: {
|
|
81
|
+
repulse: {
|
|
82
|
+
distance: 100,
|
|
83
|
+
duration: 0.4
|
|
84
|
+
},
|
|
85
|
+
push: {
|
|
86
|
+
quantity: 4
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
retina_detect: true
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<Particles
|
|
95
|
+
id="tsparticles"
|
|
96
|
+
className="pointer-events-none"
|
|
97
|
+
options={options}
|
|
98
|
+
/>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as LabelPrimitive from "@radix-ui/react-label"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
|
|
8
|
+
function Label({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}) {
|
|
12
|
+
return (
|
|
13
|
+
<LabelPrimitive.Root
|
|
14
|
+
data-slot="label"
|
|
15
|
+
className={cn(
|
|
16
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { Label }
|
|
@@ -2,11 +2,12 @@ import { VideoConference } from "@/library/pages/VideoConference";
|
|
|
2
2
|
import { ReservationsOverview } from "@/library/pages/ReservationsOverview";
|
|
3
3
|
import { FitnessOverview } from "@/library/pages/FitnessPage";
|
|
4
4
|
import { GroupChat } from "@/library/pages/GroupChat";
|
|
5
|
-
|
|
5
|
+
import { LoginDemo } from "@/components/login-template";
|
|
6
6
|
|
|
7
7
|
export const components = {
|
|
8
8
|
VideoConference,
|
|
9
|
-
GroupChat,
|
|
9
|
+
// GroupChat,
|
|
10
|
+
LoginDemo,
|
|
10
11
|
// ReservationsOverview,
|
|
11
12
|
// FitnessOverview
|
|
12
13
|
};
|