create-nexa-app 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 +273 -0
- package/bin/nexa.js +1253 -0
- package/package.json +43 -0
- package/template/jsconfig.json +12 -0
- package/template/nexa.config.js +21 -0
- package/template/public/icons/icon-192.png +0 -0
- package/template/public/icons/icon-512.png +0 -0
- package/template/public/index.html +15 -0
- package/template/public/manifest.json +28 -0
- package/template/public/nexa.svg +55 -0
- package/template/public/sw.js +53 -0
- package/template/src/App.css +117 -0
- package/template/src/App.jsx +44 -0
- package/template/src/assets/nexa.svg +55 -0
- package/template/src/components/DynamicHeader/DynamicHeader.css +55 -0
- package/template/src/components/DynamicHeader/DynamicHeader.jsx +39 -0
- package/template/src/components/Home/Home.css +185 -0
- package/template/src/components/Home/Home.jsx +25 -0
- package/template/src/components/Navbar/Navbar.css +217 -0
- package/template/src/components/Navbar/Navbar.jsx +85 -0
- package/template/src/components/Navbar/index.js +2 -0
- package/template/src/components/Nexa/Nexa.css +116 -0
- package/template/src/components/Nexa/Nexa.jsx +56 -0
- package/template/src/config/routeMeta.js +16 -0
- package/template/src/index.css +0 -0
- package/template/src/main.jsx +26 -0
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-nexa-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create a new Nexa app with prebuilt structure, PWA support, and modern React/Vite setup",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-nexa-app": "bin/nexa.js",
|
|
7
|
+
"nexa": "bin/nexa.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"template",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"start": "node ./bin/nexa.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"cli",
|
|
20
|
+
"scaffold",
|
|
21
|
+
"react",
|
|
22
|
+
"vite",
|
|
23
|
+
"nexa",
|
|
24
|
+
"create-nexa-app",
|
|
25
|
+
"pwa",
|
|
26
|
+
"starter",
|
|
27
|
+
"frontend"
|
|
28
|
+
],
|
|
29
|
+
"author": "Salman Saeed, Conscious Neurons LLC",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"preferGlobal": true,
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/algosal/create-nexa-app.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/algosal/create-nexa-app#readme",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/algosal/create-nexa-app/issues"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: nexa.config.js
|
|
3
|
+
* Purpose: Vite configuration for Nexa-generated apps.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { defineConfig } from "vite";
|
|
7
|
+
import react from "@vitejs/plugin-react";
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
root: ".",
|
|
11
|
+
base: "./",
|
|
12
|
+
plugins: [react()],
|
|
13
|
+
server: {
|
|
14
|
+
port: 5725,
|
|
15
|
+
},
|
|
16
|
+
build: {
|
|
17
|
+
outDir: "dist",
|
|
18
|
+
emptyOutDir: true,
|
|
19
|
+
},
|
|
20
|
+
clearScreen: false,
|
|
21
|
+
});
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="theme-color" content="#3ee7ff" />
|
|
7
|
+
<link rel="manifest" href="./manifest.json" />
|
|
8
|
+
<link rel="icon" href="./nexa.svg" type="image/svg+xml" />
|
|
9
|
+
<title>Nexa</title>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
<script type="module" src="/src/main.jsx"></script>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Nexa App",
|
|
3
|
+
"short_name": "Nexa",
|
|
4
|
+
"start_url": "/",
|
|
5
|
+
"display": "standalone",
|
|
6
|
+
"background_color": "#07111f",
|
|
7
|
+
"theme_color": "#3ee7ff",
|
|
8
|
+
"icons": [
|
|
9
|
+
{
|
|
10
|
+
"src": "/icons/icon-192.png",
|
|
11
|
+
"sizes": "192x192",
|
|
12
|
+
"type": "image/png",
|
|
13
|
+
"purpose": "any maskable"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"src": "/icons/icon-512.png",
|
|
17
|
+
"sizes": "512x512",
|
|
18
|
+
"type": "image/png",
|
|
19
|
+
"purpose": "any maskable"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"src": "/nexa.svg",
|
|
23
|
+
"sizes": "any",
|
|
24
|
+
"type": "image/svg+xml",
|
|
25
|
+
"purpose": "any"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="0" y1="0" x2="512" y2="512" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop offset="0%" stop-color="#040B16"></stop>
|
|
5
|
+
<stop offset="100%" stop-color="#07111F"></stop>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
|
|
8
|
+
<linearGradient id="leftStroke" x1="110" y1="120" x2="220" y2="390" gradientUnits="userSpaceOnUse">
|
|
9
|
+
<stop offset="0%" stop-color="#F4F8FF"></stop>
|
|
10
|
+
<stop offset="55%" stop-color="#C9D7E8"></stop>
|
|
11
|
+
<stop offset="100%" stop-color="#7E8FA8"></stop>
|
|
12
|
+
</linearGradient>
|
|
13
|
+
|
|
14
|
+
<linearGradient id="rightStroke" x1="260" y1="120" x2="415" y2="390" gradientUnits="userSpaceOnUse">
|
|
15
|
+
<stop offset="0%" stop-color="#4EEBFF"></stop>
|
|
16
|
+
<stop offset="55%" stop-color="#1788FF"></stop>
|
|
17
|
+
<stop offset="100%" stop-color="#0A2A8B"></stop>
|
|
18
|
+
</linearGradient>
|
|
19
|
+
|
|
20
|
+
<filter id="blueGlow" x="120" y="80" width="320" height="340" filterUnits="userSpaceOnUse">
|
|
21
|
+
<feGaussianBlur stdDeviation="18" result="blur"></feGaussianBlur>
|
|
22
|
+
<feColorMatrix in="blur" type="matrix" values="0 0 0 0 0.18
|
|
23
|
+
0 0 0 0 0.74
|
|
24
|
+
0 0 0 0 1
|
|
25
|
+
0 0 0 0.45 0"></feColorMatrix>
|
|
26
|
+
</filter>
|
|
27
|
+
|
|
28
|
+
<filter id="whiteGlow" x="70" y="80" width="230" height="340" filterUnits="userSpaceOnUse">
|
|
29
|
+
<feGaussianBlur stdDeviation="16" result="blur"></feGaussianBlur>
|
|
30
|
+
<feColorMatrix in="blur" type="matrix" values="0 0 0 0 0.85
|
|
31
|
+
0 0 0 0 0.9
|
|
32
|
+
0 0 0 0 1
|
|
33
|
+
0 0 0 0.22 0"></feColorMatrix>
|
|
34
|
+
</filter>
|
|
35
|
+
</defs>
|
|
36
|
+
|
|
37
|
+
<rect width="512" height="512" rx="96" fill="url(#bg)"></rect>
|
|
38
|
+
|
|
39
|
+
<g filter="url(#whiteGlow)">
|
|
40
|
+
<path d="M126 370L178 145C182 129 196 118 213 118H225C242 118 258 127 267 141L332 249L305 292L213 154L165 364L126 370Z" fill="url(#leftStroke)" opacity="0.9"></path>
|
|
41
|
+
</g>
|
|
42
|
+
|
|
43
|
+
<g filter="url(#blueGlow)">
|
|
44
|
+
<path d="M233 140C242 127 258 118 275 118H387L316 393H280C262 393 245 384 236 369L170 262L198 219L289 356L345 154H275C258 154 246 148 233 140Z" fill="url(#rightStroke)"></path>
|
|
45
|
+
</g>
|
|
46
|
+
|
|
47
|
+
<path d="M126 370L178 145C182 129 196 118 213 118H225C242 118 258 127 267 141L332 249L305 292L213 154L165 364L126 370Z" fill="url(#leftStroke)"></path>
|
|
48
|
+
|
|
49
|
+
<path d="M233 140C242 127 258 118 275 118H387L316 393H280C262 393 245 384 236 369L170 262L198 219L289 356L345 154H275C258 154 246 148 233 140Z" fill="url(#rightStroke)"></path>
|
|
50
|
+
|
|
51
|
+
<path d="M322 156L358 156L300 380L279 380L322 156Z" fill="white" opacity="0.12"></path>
|
|
52
|
+
|
|
53
|
+
<circle cx="355" cy="143" r="10" fill="white" opacity="0.95"></circle>
|
|
54
|
+
<circle cx="355" cy="143" r="22" fill="#5BEFFF" opacity="0.18"></circle>
|
|
55
|
+
</svg>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const CACHE_NAME = "nexa-cache-v1";
|
|
2
|
+
|
|
3
|
+
const ASSETS_TO_CACHE = ["/", "/manifest.json", "/nexa.svg"];
|
|
4
|
+
|
|
5
|
+
self.addEventListener("install", (event) => {
|
|
6
|
+
event.waitUntil(
|
|
7
|
+
caches.open(CACHE_NAME).then((cache) => {
|
|
8
|
+
return cache.addAll(ASSETS_TO_CACHE);
|
|
9
|
+
}),
|
|
10
|
+
);
|
|
11
|
+
self.skipWaiting();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
self.addEventListener("activate", (event) => {
|
|
15
|
+
event.waitUntil(
|
|
16
|
+
caches.keys().then((keys) =>
|
|
17
|
+
Promise.all(
|
|
18
|
+
keys.map((key) => {
|
|
19
|
+
if (key !== CACHE_NAME) {
|
|
20
|
+
return caches.delete(key);
|
|
21
|
+
}
|
|
22
|
+
}),
|
|
23
|
+
),
|
|
24
|
+
),
|
|
25
|
+
);
|
|
26
|
+
self.clients.claim();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
self.addEventListener("fetch", (event) => {
|
|
30
|
+
if (event.request.method !== "GET") return;
|
|
31
|
+
|
|
32
|
+
event.respondWith(
|
|
33
|
+
caches.match(event.request).then((cachedResponse) => {
|
|
34
|
+
if (cachedResponse) {
|
|
35
|
+
return cachedResponse;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return fetch(event.request)
|
|
39
|
+
.then((networkResponse) => {
|
|
40
|
+
const responseClone = networkResponse.clone();
|
|
41
|
+
|
|
42
|
+
caches.open(CACHE_NAME).then((cache) => {
|
|
43
|
+
cache.put(event.request, responseClone);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return networkResponse;
|
|
47
|
+
})
|
|
48
|
+
.catch(() => {
|
|
49
|
+
return caches.match("/");
|
|
50
|
+
});
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
53
|
+
});
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--nexa-bg: #07111f;
|
|
3
|
+
--nexa-bg-elevated: #0d1b2d;
|
|
4
|
+
--nexa-surface: rgba(255, 255, 255, 0.04);
|
|
5
|
+
|
|
6
|
+
--nexa-primary: #3ee7ff;
|
|
7
|
+
--nexa-primary-soft: #8bf4ff;
|
|
8
|
+
|
|
9
|
+
--nexa-accent: #ffcf5a;
|
|
10
|
+
--nexa-accent-soft: #ffe29a;
|
|
11
|
+
|
|
12
|
+
--nexa-text: #eaf4ff;
|
|
13
|
+
--nexa-text-dim: #93a7bd;
|
|
14
|
+
|
|
15
|
+
--nexa-border: rgba(62, 231, 255, 0.18);
|
|
16
|
+
--nexa-glow: rgba(62, 231, 255, 0.28);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
*,
|
|
20
|
+
*::before,
|
|
21
|
+
*::after {
|
|
22
|
+
box-sizing: border-box;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
html,
|
|
26
|
+
body,
|
|
27
|
+
#root {
|
|
28
|
+
margin: 0;
|
|
29
|
+
padding: 0;
|
|
30
|
+
min-height: 100%;
|
|
31
|
+
width: 100%;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
body {
|
|
35
|
+
background: var(--nexa-bg);
|
|
36
|
+
color: var(--nexa-text);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
*::-webkit-scrollbar {
|
|
40
|
+
display: none;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* ====== App Shell ====== */
|
|
44
|
+
.app-container {
|
|
45
|
+
--app-sidebar-width: 220px;
|
|
46
|
+
|
|
47
|
+
font-family: "Share Tech Mono", monospace;
|
|
48
|
+
background: var(--nexa-bg);
|
|
49
|
+
color: var(--nexa-text);
|
|
50
|
+
min-height: 100vh;
|
|
51
|
+
position: relative;
|
|
52
|
+
line-height: 1.6;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* ====== Content Wrapper ====== */
|
|
56
|
+
.app-scrollable-content {
|
|
57
|
+
margin-left: var(--app-sidebar-width);
|
|
58
|
+
min-height: 100vh;
|
|
59
|
+
width: calc(100% - var(--app-sidebar-width));
|
|
60
|
+
padding: 0;
|
|
61
|
+
transition:
|
|
62
|
+
margin-left 0.3s ease,
|
|
63
|
+
width 0.3s ease,
|
|
64
|
+
padding 0.3s ease;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* ====== Main Content Area ====== */
|
|
68
|
+
.app-main-content {
|
|
69
|
+
width: 100%;
|
|
70
|
+
min-height: 100vh;
|
|
71
|
+
padding: 0;
|
|
72
|
+
transition: padding 0.3s ease;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* ====== Utility Text Classes ====== */
|
|
76
|
+
.app-text-highlight {
|
|
77
|
+
color: var(--nexa-accent);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.app-text-cyan {
|
|
81
|
+
color: var(--nexa-primary);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.app-link {
|
|
85
|
+
color: var(--nexa-primary);
|
|
86
|
+
text-decoration: none;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.app-link:hover {
|
|
90
|
+
color: var(--nexa-accent);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* ====== Optional Shared Surface Utility ====== */
|
|
94
|
+
.app-surface {
|
|
95
|
+
background: var(--nexa-surface);
|
|
96
|
+
border: 1px solid var(--nexa-border);
|
|
97
|
+
border-radius: 12px;
|
|
98
|
+
backdrop-filter: blur(10px);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* ====== Mobile Adjustments ====== */
|
|
102
|
+
@media (max-width: 768px) {
|
|
103
|
+
.app-scrollable-content {
|
|
104
|
+
margin-left: 0;
|
|
105
|
+
width: 100%;
|
|
106
|
+
padding: 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.app-main-content {
|
|
110
|
+
width: 100%;
|
|
111
|
+
padding: 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.app-dynamic-header {
|
|
115
|
+
display: block;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Routes, Route } from "react-router-dom";
|
|
3
|
+
import AppNavbar from "./components/Navbar/Navbar";
|
|
4
|
+
import AppDynamicHeader from "./components/DynamicHeader/DynamicHeader";
|
|
5
|
+
import Home from "./components/Home/Home";
|
|
6
|
+
import Nexa from "./components/Nexa/Nexa";
|
|
7
|
+
import "./App.css";
|
|
8
|
+
|
|
9
|
+
const MOBILE_BREAKPOINT = 768;
|
|
10
|
+
|
|
11
|
+
const App = () => {
|
|
12
|
+
const getIsMobile = () =>
|
|
13
|
+
typeof window !== "undefined" && window.innerWidth <= MOBILE_BREAKPOINT;
|
|
14
|
+
|
|
15
|
+
const [isMobile, setIsMobile] = useState(() => getIsMobile());
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const handleResize = () => {
|
|
19
|
+
setIsMobile(getIsMobile());
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
window.addEventListener("resize", handleResize);
|
|
23
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="app-container">
|
|
28
|
+
<AppNavbar isMobile={isMobile} />
|
|
29
|
+
|
|
30
|
+
<div className="app-scrollable-content">
|
|
31
|
+
<AppDynamicHeader isMobile={isMobile} />
|
|
32
|
+
|
|
33
|
+
<main className="app-main-content">
|
|
34
|
+
<Routes>
|
|
35
|
+
<Route path="/" element={<Home />} />
|
|
36
|
+
<Route path="/nexa" element={<Nexa />} />
|
|
37
|
+
</Routes>
|
|
38
|
+
</main>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default App;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="0" y1="0" x2="512" y2="512" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop offset="0%" stop-color="#040B16"></stop>
|
|
5
|
+
<stop offset="100%" stop-color="#07111F"></stop>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
|
|
8
|
+
<linearGradient id="leftStroke" x1="110" y1="120" x2="220" y2="390" gradientUnits="userSpaceOnUse">
|
|
9
|
+
<stop offset="0%" stop-color="#F4F8FF"></stop>
|
|
10
|
+
<stop offset="55%" stop-color="#C9D7E8"></stop>
|
|
11
|
+
<stop offset="100%" stop-color="#7E8FA8"></stop>
|
|
12
|
+
</linearGradient>
|
|
13
|
+
|
|
14
|
+
<linearGradient id="rightStroke" x1="260" y1="120" x2="415" y2="390" gradientUnits="userSpaceOnUse">
|
|
15
|
+
<stop offset="0%" stop-color="#4EEBFF"></stop>
|
|
16
|
+
<stop offset="55%" stop-color="#1788FF"></stop>
|
|
17
|
+
<stop offset="100%" stop-color="#0A2A8B"></stop>
|
|
18
|
+
</linearGradient>
|
|
19
|
+
|
|
20
|
+
<filter id="blueGlow" x="120" y="80" width="320" height="340" filterUnits="userSpaceOnUse">
|
|
21
|
+
<feGaussianBlur stdDeviation="18" result="blur"></feGaussianBlur>
|
|
22
|
+
<feColorMatrix in="blur" type="matrix" values="0 0 0 0 0.18
|
|
23
|
+
0 0 0 0 0.74
|
|
24
|
+
0 0 0 0 1
|
|
25
|
+
0 0 0 0.45 0"></feColorMatrix>
|
|
26
|
+
</filter>
|
|
27
|
+
|
|
28
|
+
<filter id="whiteGlow" x="70" y="80" width="230" height="340" filterUnits="userSpaceOnUse">
|
|
29
|
+
<feGaussianBlur stdDeviation="16" result="blur"></feGaussianBlur>
|
|
30
|
+
<feColorMatrix in="blur" type="matrix" values="0 0 0 0 0.85
|
|
31
|
+
0 0 0 0 0.9
|
|
32
|
+
0 0 0 0 1
|
|
33
|
+
0 0 0 0.22 0"></feColorMatrix>
|
|
34
|
+
</filter>
|
|
35
|
+
</defs>
|
|
36
|
+
|
|
37
|
+
<rect width="512" height="512" rx="96" fill="url(#bg)"></rect>
|
|
38
|
+
|
|
39
|
+
<g filter="url(#whiteGlow)">
|
|
40
|
+
<path d="M126 370L178 145C182 129 196 118 213 118H225C242 118 258 127 267 141L332 249L305 292L213 154L165 364L126 370Z" fill="url(#leftStroke)" opacity="0.9"></path>
|
|
41
|
+
</g>
|
|
42
|
+
|
|
43
|
+
<g filter="url(#blueGlow)">
|
|
44
|
+
<path d="M233 140C242 127 258 118 275 118H387L316 393H280C262 393 245 384 236 369L170 262L198 219L289 356L345 154H275C258 154 246 148 233 140Z" fill="url(#rightStroke)"></path>
|
|
45
|
+
</g>
|
|
46
|
+
|
|
47
|
+
<path d="M126 370L178 145C182 129 196 118 213 118H225C242 118 258 127 267 141L332 249L305 292L213 154L165 364L126 370Z" fill="url(#leftStroke)"></path>
|
|
48
|
+
|
|
49
|
+
<path d="M233 140C242 127 258 118 275 118H387L316 393H280C262 393 245 384 236 369L170 262L198 219L289 356L345 154H275C258 154 246 148 233 140Z" fill="url(#rightStroke)"></path>
|
|
50
|
+
|
|
51
|
+
<path d="M322 156L358 156L300 380L279 380L322 156Z" fill="white" opacity="0.12"></path>
|
|
52
|
+
|
|
53
|
+
<circle cx="355" cy="143" r="10" fill="white" opacity="0.95"></circle>
|
|
54
|
+
<circle cx="355" cy="143" r="22" fill="#5BEFFF" opacity="0.18"></circle>
|
|
55
|
+
</svg>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
.app-dynamic-header {
|
|
2
|
+
position: sticky;
|
|
3
|
+
top: 0;
|
|
4
|
+
z-index: 2000;
|
|
5
|
+
width: 100%;
|
|
6
|
+
background: #0a0f24;
|
|
7
|
+
border-bottom: 1px solid #00ffff;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.app-dh-inner {
|
|
11
|
+
padding: 20px 24px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.app-dh-title {
|
|
15
|
+
margin: 0;
|
|
16
|
+
font-size: 26px;
|
|
17
|
+
font-weight: 700;
|
|
18
|
+
color: #00ffff;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.app-dh-subtitle {
|
|
22
|
+
margin-top: 6px;
|
|
23
|
+
font-size: 13px;
|
|
24
|
+
color: #9ca3af;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.app-dh-highlight {
|
|
28
|
+
color: #ffd700;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Tooltip */
|
|
32
|
+
.app-dh-title-wrapper {
|
|
33
|
+
position: relative;
|
|
34
|
+
display: inline-block;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.app-dh-tooltip {
|
|
38
|
+
visibility: hidden;
|
|
39
|
+
opacity: 0;
|
|
40
|
+
position: absolute;
|
|
41
|
+
bottom: -28px;
|
|
42
|
+
left: 0;
|
|
43
|
+
background: #000;
|
|
44
|
+
color: #00ffff;
|
|
45
|
+
padding: 6px 10px;
|
|
46
|
+
font-size: 12px;
|
|
47
|
+
border-radius: 4px;
|
|
48
|
+
white-space: nowrap;
|
|
49
|
+
transition: opacity 0.2s ease;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.app-dh-title-wrapper:hover .app-dh-tooltip {
|
|
53
|
+
visibility: visible;
|
|
54
|
+
opacity: 1;
|
|
55
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useLocation } from "react-router-dom";
|
|
3
|
+
import { routeMeta } from "../../config/routeMeta";
|
|
4
|
+
import "./DynamicHeader.css";
|
|
5
|
+
|
|
6
|
+
const AppDynamicHeader = ({ isMobile }) => {
|
|
7
|
+
const location = useLocation();
|
|
8
|
+
|
|
9
|
+
const currentMeta = routeMeta[location.pathname] || {
|
|
10
|
+
title: "Nexa",
|
|
11
|
+
subtitle: "",
|
|
12
|
+
tooltip: "",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<header
|
|
17
|
+
key={location.pathname}
|
|
18
|
+
className={`app-dynamic-header ${isMobile ? "app-dynamic-header-mobile" : ""}`}
|
|
19
|
+
>
|
|
20
|
+
<div className="app-dh-inner">
|
|
21
|
+
<div className="app-dh-title-wrapper">
|
|
22
|
+
<h1 className="app-dh-title" title={currentMeta.tooltip || ""}>
|
|
23
|
+
{currentMeta.title}
|
|
24
|
+
</h1>
|
|
25
|
+
|
|
26
|
+
{currentMeta.tooltip && (
|
|
27
|
+
<span className="app-dh-tooltip">{currentMeta.tooltip}</span>
|
|
28
|
+
)}
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
{currentMeta.subtitle && (
|
|
32
|
+
<p className="app-dh-subtitle">{currentMeta.subtitle}</p>
|
|
33
|
+
)}
|
|
34
|
+
</div>
|
|
35
|
+
</header>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default AppDynamicHeader;
|