generated from Gallardo7761/miarma-template-full
Add: experimental mobile version
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import '@/css/NavBar.css';
|
||||
import NavItem from './NavItem';
|
||||
import LanguageButton from '../LanguageButton';
|
||||
import { useWindowWidth } from '@/hooks/useWindowWidth';
|
||||
import NavBarDesktop from '@/components/NavBar/desktop/NavBarDesktop';
|
||||
import NavBarMobile from '@/components/NavBar/mobile/NavBarMobile';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const NavBar = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const width = useWindowWidth();
|
||||
const navItems = [
|
||||
{ label: t("nav.inicio.label"), href: `/#${t("nav.inicio.href")}` },
|
||||
{ label: t("nav.muestras.label"), href: `/#${t("nav.muestras.href")}` },
|
||||
@@ -14,26 +14,12 @@ const NavBar = () => {
|
||||
{ label: t("nav.login.label"), href: `/${t("nav.login.href")}` }
|
||||
];
|
||||
|
||||
const centerItems = navItems.slice(0, navItems.length - 1);
|
||||
const rightItems = navItems.slice(-1);
|
||||
|
||||
return (
|
||||
<nav className="nav-container">
|
||||
<ul className="d-flex align-items-center list-unstyled m-0 w-100 justify-content-center position-relative">
|
||||
|
||||
{centerItems.map((item, index) => (
|
||||
<NavItem key={index} item={item} index={index} total={centerItems.length} />
|
||||
))}
|
||||
|
||||
<li className="position-absolute end-0 d-flex">
|
||||
<LanguageButton as='navitem' index={0} total={rightItems.length + 1} />
|
||||
{rightItems.map((item, index) => (
|
||||
<NavItem key={index} item={item} index={index + 1} total={rightItems.length + 1} />
|
||||
))}
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
return width > 991.98 ? (
|
||||
<nav className="nav-container p-0">
|
||||
<NavBarDesktop navItems={navItems} />
|
||||
</nav>
|
||||
) : (
|
||||
<NavBarMobile navItems={navItems} />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { HashLink } from "react-router-hash-link/dist/react-router-hash-link.cjs.production";
|
||||
|
||||
const NavItem = ({ item, index, onClick }) => {
|
||||
const NavItem = ({ item, index, onClick, onCloseNav }) => {
|
||||
let borderClass = `${index === 0 ? "border-left border-right" : "border-right"}`;
|
||||
|
||||
const handleClick = (e) => {
|
||||
@@ -10,6 +10,10 @@ const NavItem = ({ item, index, onClick }) => {
|
||||
} else if (item.href === "#") {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (onCloseNav) {
|
||||
onCloseNav();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
30
src/components/NavBar/desktop/NavBarDesktop.jsx
Normal file
30
src/components/NavBar/desktop/NavBarDesktop.jsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import NavItem from '@/components/NavBar/NavItem';
|
||||
import LanguageButton from '@/components/LanguageButton';
|
||||
|
||||
import '@/css/NavBar.css';
|
||||
|
||||
const NavBarDesktop = ({ navItems }) => {
|
||||
const centerItems = navItems.slice(0, navItems.length - 1);
|
||||
const rightItems = navItems.slice(-1);
|
||||
|
||||
return (
|
||||
<ul className="d-flex align-items-center list-unstyled m-0 w-100 justify-content-center position-relative">
|
||||
<li className="position-absolute start-0 d-flex">
|
||||
<LanguageButton as='navitem' index={0} total={rightItems.length + 1} />
|
||||
</li>
|
||||
|
||||
{centerItems.map((item, index) => (
|
||||
<NavItem key={index} item={item} index={index} total={centerItems.length} />
|
||||
))}
|
||||
|
||||
<li className="position-absolute end-0 d-flex">
|
||||
{rightItems.map((item, index) => (
|
||||
<NavItem key={index} item={item} index={index} total={rightItems.length} />
|
||||
))}
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavBarDesktop;
|
||||
41
src/components/NavBar/mobile/NavBarMobile.jsx
Normal file
41
src/components/NavBar/mobile/NavBarMobile.jsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useState } from 'react';
|
||||
import NavItem from '@/components/NavBar/NavItem';
|
||||
import LanguageButton from '@/components/LanguageButton';
|
||||
import '@/css/NavBarMobile.css';
|
||||
|
||||
const NavBarMobile = ({ navItems }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="nav-container d-lg-none">
|
||||
<div className="nav-mobile-header d-flex justify-content-between align-items-center">
|
||||
{/* Por si algun dia se pone
|
||||
<div className="nav-mobile-brand"></div>
|
||||
*/}
|
||||
<button
|
||||
className={`nav-mobile-toggle ms-2 ${open ? 'open' : ''}`}
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{open && (
|
||||
<ul className="nav-list-mobile d-flex flex-column align-items-center list-unstyled m-0 w-100">
|
||||
{navItems.map((item, index) => (
|
||||
<NavItem key={index} item={item} onCloseNav={handleClose} />
|
||||
))}
|
||||
<LanguageButton as="navitem" />
|
||||
</ul>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavBarMobile;
|
||||
@@ -23,4 +23,31 @@ header p {
|
||||
letter-spacing: 3px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 5px var(--blood-god);
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
header h1 {
|
||||
font-size: 2.6rem;
|
||||
letter-spacing: 4px;
|
||||
}
|
||||
|
||||
header p {
|
||||
font-size: 1rem;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
header {
|
||||
padding: 2.5rem 1rem !important;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2rem;
|
||||
letter-spacing: 3px;
|
||||
}
|
||||
|
||||
header p {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
@@ -73,4 +73,25 @@ input.form-control:focus {
|
||||
border-color: var(--plasma-glow) !important;
|
||||
box-shadow: 0 0 20px var(--blood-god) !important;
|
||||
text-shadow: 0 0 5px #fff !important;
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.login-card {
|
||||
padding: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
.login-card {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-card h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.login-button {
|
||||
padding: 12px 24px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
67
src/css/NavBarMobile.css
Normal file
67
src/css/NavBarMobile.css
Normal file
@@ -0,0 +1,67 @@
|
||||
.nav-container {
|
||||
background-color: #0f1114;
|
||||
border-bottom: 1px solid var(--dirty-gold);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 900;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nav-mobile-header {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.nav-mobile-brand {
|
||||
color: #666;
|
||||
font-family: 'Cinzel', serif;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.nav-mobile-toggle {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.nav-mobile-toggle span {
|
||||
display: block;
|
||||
width: 25px;
|
||||
height: 2px;
|
||||
background-color: var(--dirty-gold);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-mobile-toggle.open span:nth-child(1) {
|
||||
transform: rotate(45deg) translate(5px, 5px);
|
||||
}
|
||||
|
||||
.nav-mobile-toggle.open span:nth-child(2) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.nav-mobile-toggle.open span:nth-child(3) {
|
||||
transform: rotate(-45deg) translate(5px, -5px);
|
||||
}
|
||||
|
||||
.nav-list-mobile {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nav-list-mobile li {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-list-mobile .nav-link-custom,
|
||||
.nav-list-mobile .nav-button {
|
||||
padding: 12px 0;
|
||||
font-size: 1rem;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #222;
|
||||
}
|
||||
@@ -28,4 +28,20 @@
|
||||
.tech-card p {
|
||||
line-height: 1.6;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.tech-card {
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
.tech-card p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
.tech-card {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
@@ -241,4 +241,68 @@ section {
|
||||
98% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
h1, h2 { animation: textFlicker 5s infinite; }
|
||||
h1, h2 { animation: textFlicker 5s infinite; }
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
section {
|
||||
padding: 60px 16px !important;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.section-title::before,
|
||||
.section-title::after {
|
||||
margin: 0 10px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.foto-placeholder {
|
||||
height: 240px;
|
||||
}
|
||||
|
||||
.btn-imperial {
|
||||
padding: 12px 28px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.tech-input,
|
||||
.tech-textarea {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
section {
|
||||
padding: 40px 12px !important;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.6rem;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.section-title::before,
|
||||
.section-title::after {
|
||||
margin: 0 8px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.foto-placeholder {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.foto-placeholder span {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.foto-caption {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.btn-imperial {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
13
src/hooks/useWindowWidth.js
Normal file
13
src/hooks/useWindowWidth.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
export const useWindowWidth = () => {
|
||||
const [width, setWidth] = useState(window.innerWidth);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => setWidth(window.innerWidth);
|
||||
window.addEventListener("resize", handleResize);
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []);
|
||||
|
||||
return width;
|
||||
};
|
||||
@@ -6,7 +6,7 @@
|
||||
"inicio": { "label": "Home", "href": "home" },
|
||||
"muestras": { "label": "Portfolio", "href": "portfolio" },
|
||||
"pedidos": { "label": "Comissions", "href": "comissions" },
|
||||
"sobre": { "label": "About Markcus", "href": "about-me" },
|
||||
"sobre": { "label": "About", "href": "about-me" },
|
||||
"login": { "label": "Login", "href": "login" }
|
||||
},
|
||||
"home": {
|
||||
@@ -30,7 +30,7 @@
|
||||
"title": "Sample C-3: Xenos Filth"
|
||||
}
|
||||
],
|
||||
"pedidos_title": "+++ Plea to the Manufactorum +++",
|
||||
"pedidos_title": "+++ Comission +++",
|
||||
"pedidos_text": "Fill in the details to request the attention of the Artificer. Be precise, time is an Emperor-limited resource.",
|
||||
"pedidos_name": "Commander Designation (Name)",
|
||||
"pedidos_email": "Vox Frequency (Email)",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
{ "icon": "⚔", "title": "Muestra C-3: Xenos Filth" }
|
||||
],
|
||||
|
||||
"pedidos_title": "+++ Súplica al Manufactorum +++",
|
||||
"pedidos_title": "+++ Comisión +++",
|
||||
"pedidos_text": "Rellena los datos para solicitar la atención del Artífice. Sé preciso, el tiempo es un recurso limitado del Emperador.",
|
||||
"pedidos_name": "Designación del Comandante (Nombre)",
|
||||
"pedidos_email": "Frecuencia Vox (Email)",
|
||||
|
||||
Reference in New Issue
Block a user