Sidebar y styles
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 223 KiB |
@@ -82,7 +82,7 @@ export default function Page() {
|
||||
|
||||
<section className="mb-5" id="input-props">
|
||||
<h2 className="h5 mb-3">Props del componente</h2>
|
||||
<table className="table table-bordered table-striped align-middle">
|
||||
<table className="table align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Prop</th>
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function Page() {
|
||||
/>
|
||||
<div className="my-5">
|
||||
<h1 className="mb-4 display-5">
|
||||
📑 Componente: <code>Select</code>
|
||||
Componente: <code>Select</code>
|
||||
</h1>
|
||||
|
||||
<p className="mb-4 text-muted">
|
||||
@@ -38,7 +38,7 @@ export default function Page() {
|
||||
<hr className="my-5" />
|
||||
|
||||
<VistaComponente
|
||||
title="🌍 Select básico"
|
||||
title="Select básico"
|
||||
description="Un select controlado con opciones simples y un placeholder deshabilitado."
|
||||
code={countrySelectSnippet}
|
||||
id="select-basico"
|
||||
@@ -54,7 +54,7 @@ export default function Page() {
|
||||
</VistaComponente>
|
||||
|
||||
<VistaComponente
|
||||
title="🎨 Personalización de estilos"
|
||||
title="Personalización de estilos"
|
||||
description="Puedes aplicar clases personalizadas al contenedor, etiqueta o al select."
|
||||
code={languageSelectSnippet}
|
||||
id="select-estilos"
|
||||
@@ -77,7 +77,7 @@ export default function Page() {
|
||||
</VistaComponente>
|
||||
|
||||
<section className="mb-5">
|
||||
<h2 className="h5">⚙️ Props del componente</h2>
|
||||
<h2 className="h5">Props del componente</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<code>label: string</code> – Etiqueta visible del campo.
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
"use client";
|
||||
import IndiceContenido from "@/components/content-index";
|
||||
import Sidebar, { SidebarLink } from "@/components/sidebar";
|
||||
import VistaComponente from "@/components/vista-componente";
|
||||
import React from "react";
|
||||
|
||||
const basicLinks: SidebarLink[] = [
|
||||
{ icon: "bi-house", text: "Inicio", href: "/inicio" },
|
||||
{
|
||||
icon: "bi-person",
|
||||
text: "Perfil",
|
||||
href: "/perfil",
|
||||
className: "hover-bg-success",
|
||||
},
|
||||
{
|
||||
icon: "bi-envelope",
|
||||
text: "Mensajes",
|
||||
href: "/mensajes",
|
||||
className: "hover-bg-info",
|
||||
},
|
||||
{
|
||||
icon: "bi-gear",
|
||||
text: "Configuración",
|
||||
href: "/configuracion",
|
||||
className: "hover-bg-warning",
|
||||
},
|
||||
{
|
||||
icon: "bi-bar-chart",
|
||||
text: "Reportes",
|
||||
href: "/reportes",
|
||||
className: "hover-bg-danger",
|
||||
},
|
||||
];
|
||||
|
||||
const onClickLinks: SidebarLink[] = [
|
||||
{
|
||||
icon: "bi-lightning",
|
||||
text: "Acción rápida",
|
||||
onClick: () => alert("¡Ejecutando acción rápida!"),
|
||||
className: "hover-bg-warning text-warning",
|
||||
},
|
||||
{
|
||||
icon: "bi-question-circle",
|
||||
text: "Ayuda",
|
||||
onClick: () => alert("Mostrando ayuda..."),
|
||||
className: "hover-bg-info text-info",
|
||||
},
|
||||
{
|
||||
icon: "bi-bell",
|
||||
text: "Notificaciones",
|
||||
onClick: () => alert("Mostrando notificaciones..."),
|
||||
className: "hover-bg-primary text-primary",
|
||||
},
|
||||
];
|
||||
|
||||
const renderItems: SidebarLink[] = [
|
||||
{
|
||||
render: () => (
|
||||
<li className="nav-item">
|
||||
<div className="nav-link text-success">
|
||||
<i className="bi bi-star-fill me-2"></i>
|
||||
<span>Componente especial</span>
|
||||
</div>
|
||||
</li>
|
||||
),
|
||||
},
|
||||
{
|
||||
render: () => (
|
||||
<li className="nav-item">
|
||||
<div className="nav-link text-primary">
|
||||
<i className="bi bi-heart-fill me-2"></i>
|
||||
<span>Favoritos</span>
|
||||
</div>
|
||||
</li>
|
||||
),
|
||||
},
|
||||
{
|
||||
render: () => (
|
||||
<li className="nav-item hover-bg-danger">
|
||||
<div className="nav-link text-danger">
|
||||
<i className="bi bi-exclamation-circle-fill me-2"></i>
|
||||
<span>Alertas</span>
|
||||
</div>
|
||||
</li>
|
||||
),
|
||||
},
|
||||
{
|
||||
render: () => (
|
||||
<li className="nav-item">
|
||||
<div className="nav-link text-warning">
|
||||
<i className="bi bi-lightbulb-fill me-2"></i>
|
||||
<span>Ideas</span>
|
||||
</div>
|
||||
</li>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<IndiceContenido
|
||||
items={[
|
||||
{ id: "sidebar-ejemplo", label: "Sidebar básico" },
|
||||
{ id: "sidebar-acciones", label: "Sidebar con acciones" },
|
||||
{
|
||||
id: "sidebar-render-items",
|
||||
label: "Sidebar con elementos personalizados",
|
||||
},
|
||||
{
|
||||
id: "sidebar-colores",
|
||||
label: "Sidebar con personalización de colores",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="my-5">
|
||||
<h1 className="mb-4 display-5">
|
||||
Componente: <code>Sidebar</code>
|
||||
</h1>
|
||||
|
||||
<p className="mb-4 text-muted">
|
||||
El <code>Sidebar</code> es un componente versátil que funciona como un
|
||||
menú lateral adaptable, diseñado para ser accesible tanto en
|
||||
dispositivos móviles como en pantallas de escritorio. Permite incluir
|
||||
enlaces, acciones personalizadas, colores personalizables por clase y
|
||||
elementos renderizados dinámicamente.
|
||||
</p>
|
||||
|
||||
<hr className="my-5"/>
|
||||
|
||||
<VistaComponente
|
||||
title="Sidebar básico"
|
||||
description="Un ejemplo básico de un componente de menú lateral con opciones simples."
|
||||
id="sidebar-ejemplo"
|
||||
>
|
||||
<div className="d-lg-flex">
|
||||
<Sidebar
|
||||
id="sidebar"
|
||||
userName="Usuario"
|
||||
userRole="Administrador"
|
||||
appTitle="Sistema de Encuestas"
|
||||
logoSrc="/unam.png"
|
||||
links={basicLinks}
|
||||
/>
|
||||
<main className="flex-grow-1 p-4 border rounded">
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</VistaComponente>
|
||||
|
||||
<VistaComponente
|
||||
title="Sidebar con acciones"
|
||||
description="Menú lateral con enlaces que ejecutan funciones al hacer clic."
|
||||
id="sidebar-acciones"
|
||||
>
|
||||
<div className="d-lg-flex">
|
||||
<Sidebar
|
||||
id="sidebar-acciones"
|
||||
userName="Usuario"
|
||||
userRole="Administrador"
|
||||
appTitle="Sistema de Encuestas"
|
||||
logoSrc="/unam.png"
|
||||
links={onClickLinks}
|
||||
/>
|
||||
<main className="flex-grow-1 p-4 border rounded">
|
||||
<h1>Contenido interactivo</h1>
|
||||
<p>
|
||||
Cada enlace ejecuta una acción mediante <code>onClick</code>.
|
||||
Este patrón es útil para accesos rápidos o elementos no
|
||||
vinculados a rutas.
|
||||
</p>
|
||||
</main>
|
||||
</div>
|
||||
</VistaComponente>
|
||||
|
||||
<VistaComponente
|
||||
title="Sidebar con elementos personalizados"
|
||||
description="Uso de la propiedad render para incluir componentes únicos dentro del menú."
|
||||
id="sidebar-render-items"
|
||||
>
|
||||
<div className="d-lg-flex">
|
||||
<Sidebar
|
||||
id="sidebar-render-items"
|
||||
userName="Usuario"
|
||||
userRole="Administrador"
|
||||
appTitle="Sistema de Encuestas"
|
||||
logoSrc="/unam.png"
|
||||
links={renderItems}
|
||||
/>
|
||||
<main className="flex-grow-1 p-4 border rounded">
|
||||
<h1>Elementos renderizados</h1>
|
||||
<p>
|
||||
La propiedad <code>render</code> permite insertar cualquier JSX
|
||||
personalizado dentro del menú, ideal para incluir botones,
|
||||
badges o estados especiales.
|
||||
</p>
|
||||
</main>
|
||||
</div>
|
||||
</VistaComponente>
|
||||
|
||||
<VistaComponente
|
||||
title="Sidebar con personalización de colores"
|
||||
description="Cada enlace puede tener un color diferente en hover usando clases utilitarias como hover-bg-warning, hover-bg-danger, etc."
|
||||
id="sidebar-colores"
|
||||
>
|
||||
<div className="d-lg-flex">
|
||||
<Sidebar
|
||||
id="sidebar-colores"
|
||||
userName="Usuario"
|
||||
userRole="Administrador"
|
||||
appTitle="Sistema de Encuestas"
|
||||
logoSrc="/unam.png"
|
||||
links={[...basicLinks, ...onClickLinks]}
|
||||
/>
|
||||
<main className="flex-grow-1 p-4 border rounded">
|
||||
<h1>Hover personalizados</h1>
|
||||
<p>
|
||||
Gracias a las clases <code>hover-bg-COLOR</code> generadas a
|
||||
partir de <code>$theme-colors</code>, cada elemento del menú
|
||||
puede mostrar un color diferente al pasar el cursor.
|
||||
</p>
|
||||
</main>
|
||||
</div>
|
||||
</VistaComponente>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import IndiceContenido from "@/components/content-index";
|
||||
import VistaComponente from "@/components/vista-componente";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<IndiceContenido
|
||||
items={[
|
||||
{ id: "box", label: "Box" },
|
||||
{ id: "gradients", label: "Gradientes" },
|
||||
{ id: "scale-hover", label: "Escala al pasar el cursor" },
|
||||
{ id: "hover-bg", label: "Color de fondo al pasar el cursor" },
|
||||
]}
|
||||
/>
|
||||
<div className="my-5">
|
||||
<h1 className="mb-4 display-5">Estilos</h1>
|
||||
|
||||
<p className="mb-4 text-muted">
|
||||
Los estilos de la aplicación se basan en Bootstrap 5 y se personalizan
|
||||
mediante clases SCSS para mantener una apariencia coherente y
|
||||
flexible.
|
||||
</p>
|
||||
|
||||
<hr className="my-5" />
|
||||
|
||||
<div className="alert alert-info">
|
||||
<i className="bi bi-info-circle me-2"></i>
|
||||
Leer la documentación de <code>.box</code>
|
||||
</div>
|
||||
<VistaComponente
|
||||
id="box"
|
||||
title="Estilo: Box"
|
||||
description="Un contenedor con bordes redondeados y sombra suave para destacar contenido. La mayoria del sistema usa este estilo, por loq ue si lo modificas muy seguramente estarias modificando la apariencia de toda la aplicacion."
|
||||
code={`<div className="box">
|
||||
<h1><code>div</code> con className <code>.box</code></h1>
|
||||
</div>`}
|
||||
>
|
||||
<div className="box mb-3">
|
||||
<h1 className="mb-0">
|
||||
<code>div</code> con className <code>.box</code>
|
||||
</h1>
|
||||
</div>
|
||||
</VistaComponente>
|
||||
|
||||
<VistaComponente
|
||||
id="gradients"
|
||||
title="Estilo: bg-gradient"
|
||||
description="Genera un fondo con gradiente en diferentes direcciones utilizando los colores del tema. Cada clase aplica una transición de color hacia el fondo general de la página (<code>$body-bg</code>). Este estilo se usa también en la página de inicio con el color azul. si se desea que el color sea mas fuerte se puede modificar en @/styles/sass/_gradients.scss"
|
||||
code={`<div className="bg-gradient-{variante}-{posicion} p-4 text-white">
|
||||
Gradiente desde arriba (top) con color primario
|
||||
</div>`}
|
||||
>
|
||||
<div className="bg-gradient-primary-top p-3 mb-3 rounded text-white">
|
||||
Gradiente desde arriba (top) con color primario
|
||||
</div>
|
||||
<div className="bg-gradient-success-right p-3 mb-3 rounded text-white">
|
||||
Gradiente desde la derecha (right) con color success
|
||||
</div>
|
||||
<div className="bg-gradient-warning-bottom p-3 mb-3 rounded text-white">
|
||||
Gradiente desde abajo (bottom) con color warning
|
||||
</div>
|
||||
<div className="bg-gradient-info-left p-3 mb-3 rounded text-white text-end">
|
||||
Gradiente desde la izquierda (left) con color info
|
||||
</div>
|
||||
|
||||
<h2 className="mt-4 h5">Ejemplo de gradiente de Bootstrap 5</h2>
|
||||
<p className="text-muted">
|
||||
Bootstrap incluye su propio estilo <code>.bg-gradient</code> que
|
||||
aplica un efecto sutil sobre el fondo.
|
||||
</p>
|
||||
<div className="bg-primary bg-gradient p-3 text-white rounded mb-3">
|
||||
Fondo con <code>.bg-primary.bg-gradient</code>
|
||||
</div>
|
||||
</VistaComponente>
|
||||
<VistaComponente
|
||||
id="scale-hover"
|
||||
title="Utilidad: scale-hover"
|
||||
description="Estas clases aplican una transformación <code>scale</code> al pasar el cursor, generando un efecto de ampliación suave. Útil para botones, tarjetas u otros elementos interactivos."
|
||||
code={`<div className="scale-hover-1">Escala 1.05</div>
|
||||
<div className="scale-hover-3">Escala 1.15</div>
|
||||
<div className="scale-hover-5">Escala 1.25</div>`}
|
||||
>
|
||||
<div className="d-flex flex-wrap gap-3 mb-3">
|
||||
{[1, 2, 3, 4, 5].map((scale) => (
|
||||
<div
|
||||
key={scale}
|
||||
className={`scale-hover-${scale} p-3 border rounded text-center`}
|
||||
style={{ width: "150px" }}
|
||||
>
|
||||
<strong>{`scale-hover-${scale}`}</strong>
|
||||
<br />({(1 + scale * 0.05).toFixed(2)}x)
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</VistaComponente>
|
||||
|
||||
<VistaComponente
|
||||
id="hover-bg"
|
||||
title="Utilidad: hover-bg"
|
||||
description="Clases que aplican un color de fondo semitransparente en estado <code>:hover</code> o cuando el elemento tiene la clase <code>.active</code>. Se pueden usar en tarjetas, botones personalizados o elementos de navegación. Además, si contienen un elemento con clase <code>.nav-link</code>, su color se forzará a blanco."
|
||||
code={`<div className="hover-bg-primary p-3">Hover primario</div>
|
||||
<div className="hover-bg-success p-3 active">Elemento activo con fondo success</div>`}
|
||||
>
|
||||
<div className="d-flex flex-wrap gap-3">
|
||||
{["primary", "success", "warning", "danger", "azul"].map(
|
||||
(color) => (
|
||||
<div
|
||||
key={color}
|
||||
className={`hover-bg-${color} p-3 border rounded text-center`}
|
||||
style={{ width: "180px" }}
|
||||
>
|
||||
<strong>{`hover-bg-${color}`}</strong>
|
||||
<br />
|
||||
{color === "primary" && "(hover o active)"}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h2 className="mt-4 h6">
|
||||
Ejemplo con <code>.nav-link</code>
|
||||
</h2>
|
||||
<div className="hover-bg-primary p-3 rounded mb-3">
|
||||
<a className="nav-link" href="#">
|
||||
Enlace dentro de hover-bg-primary
|
||||
</a>
|
||||
</div>
|
||||
</VistaComponente>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -224,7 +224,7 @@ export default function Page() {
|
||||
{/* PROPS */}
|
||||
<section className="mb-5" id="props-del-componente">
|
||||
<h2 className="h5 mb-3">Props del componente</h2>
|
||||
<table className="table table-bordered table-striped align-middle">
|
||||
<table className="table align-middle overflow-auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Prop</th>
|
||||
|
||||
@@ -3,6 +3,8 @@ import { Roboto } from "next/font/google";
|
||||
|
||||
import 'bootstrap-icons/font/bootstrap-icons.css';
|
||||
import '@/styles/sass/bootstrap.scss';
|
||||
import BootstrapClient from "@/components/bootstrap-client";
|
||||
|
||||
import Navbar from "@/components/navbar";
|
||||
|
||||
const roboto = Roboto({
|
||||
@@ -26,6 +28,7 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${roboto.className}`}>
|
||||
<BootstrapClient />
|
||||
<Navbar />
|
||||
{children}
|
||||
</body>
|
||||
|
||||
+63
-9
@@ -1,11 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import ComponentCard from "@/components/component-card";
|
||||
import Image from "next/image";
|
||||
import React from "react";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { vscDarkPlus } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<main className="">
|
||||
<main>
|
||||
<div className="container-fluid bg-gradient-primary-bottom text-center py-5">
|
||||
<div className="mt-4">
|
||||
<Image
|
||||
@@ -16,10 +19,62 @@ export default function Page() {
|
||||
className="img-fluid"
|
||||
/>
|
||||
</div>
|
||||
<h1 className="display-3 fw-bold text-azul">Libreria de componentes de CIDWA</h1>
|
||||
<h1 className="display-3 fw-bold text-azul">
|
||||
Librería de componentes de CIDWA
|
||||
</h1>
|
||||
</div>
|
||||
<div className="container">
|
||||
<h2>Componentes</h2>
|
||||
|
||||
<div className="container py-4">
|
||||
<h1>Uso de componentes</h1>
|
||||
<p>
|
||||
Esta librería ofrece una colección de componentes reutilizables, diseñados según las guías de estilo de CIDWA, para facilitar la construcción de interfaces consistentes y accesibles.
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>Instalación</h2>
|
||||
<p>
|
||||
Para comenzar a utilizar los componentes, primero instala las siguientes dependencias:
|
||||
</p>
|
||||
|
||||
<SyntaxHighlighter language="bash" style={vscDarkPlus} customStyle={{ borderRadius: "10px" }}>
|
||||
{`npm i bootstrap bootstrap-icons
|
||||
npm i -D sass`}
|
||||
</SyntaxHighlighter>
|
||||
|
||||
<p>
|
||||
Luego, copia las carpetas <code>components</code> y <code>styles</code> dentro de tu directorio <code>src</code>. Estas contienen los componentes y estilos necesarios (SCSS y CSS).
|
||||
</p>
|
||||
|
||||
<h2>Configuración</h2>
|
||||
<p>
|
||||
Asegúrate de importar los estilos y el componente <code>BootstrapClient</code> en tu archivo <code>layout.tsx</code>, como se muestra a continuación:
|
||||
</p>
|
||||
|
||||
<SyntaxHighlighter language="typescript" style={vscDarkPlus} customStyle={{ borderRadius: "10px" }}>
|
||||
{`import 'bootstrap-icons/font/bootstrap-icons.css';
|
||||
import '@/styles/sass/bootstrap.scss';
|
||||
import BootstrapClient from "@/components/bootstrap-client";`}
|
||||
</SyntaxHighlighter>
|
||||
|
||||
<p>
|
||||
Es importante incluir el componente <code>BootstrapClient</code> para que los scripts de Bootstrap se carguen correctamente y sus componentes interactivos funcionen como se espera.
|
||||
</p>
|
||||
|
||||
<p>Aquí tienes un ejemplo del uso dentro del layout:</p>
|
||||
|
||||
<SyntaxHighlighter language="tsx" style={vscDarkPlus} customStyle={{ borderRadius: "10px" }}>
|
||||
{`<html lang="en">
|
||||
<body className={\`\${roboto.className}\`}>
|
||||
<BootstrapClient />
|
||||
{children}
|
||||
</body>
|
||||
</html>`}
|
||||
</SyntaxHighlighter>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>Componentes disponibles</h2>
|
||||
<div className="row row-gap-4">
|
||||
<div className="col-lg-4">
|
||||
<ComponentCard title="Table" src="/table.png" href="table" />
|
||||
@@ -28,11 +83,7 @@ export default function Page() {
|
||||
<ComponentCard title="Input" src="/input.png" href="input" />
|
||||
</div>
|
||||
<div className="col-lg-4">
|
||||
<ComponentCard
|
||||
title="Pagination"
|
||||
src="/pagination.png"
|
||||
href="pagination"
|
||||
/>
|
||||
<ComponentCard title="Pagination" src="/pagination.png" href="pagination" />
|
||||
</div>
|
||||
<div className="col-lg-4">
|
||||
<ComponentCard title="Options" src="/options.png" href="options" />
|
||||
@@ -40,6 +91,9 @@ export default function Page() {
|
||||
<div className="col-lg-4">
|
||||
<ComponentCard title="Select" src="/select.png" href="select" />
|
||||
</div>
|
||||
<div className="col-lg-4">
|
||||
<ComponentCard title="Sidebar" src="/sidebar.png" href="sidebar" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
'use client';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default function BootstrapClient() {
|
||||
useEffect(() => {
|
||||
const loadBootstrap = async () => {
|
||||
if (typeof document !== 'undefined') {
|
||||
// @ts-expect-error: Bootstrap is loaded dynamically
|
||||
await import('bootstrap/dist/js/bootstrap.bundle.min.js');
|
||||
}
|
||||
};
|
||||
|
||||
loadBootstrap();
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
+60
-33
@@ -5,39 +5,66 @@ const Navbar: React.FC = () => {
|
||||
return (
|
||||
<nav className="navbar navbar-expand-lg navbar-light bg-transparent fixed-top">
|
||||
<div className="container">
|
||||
<div className="mx-auto">
|
||||
<ul className="navbar-nav gap-5">
|
||||
<li className="nav-item">
|
||||
<Link className="nav-link" href="/">
|
||||
Inicio
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<Link className="nav-link" href="/table">
|
||||
Table
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<Link className="nav-link" href="/input">
|
||||
Input
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<Link className="nav-link" href="/pagination">
|
||||
Pagination
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<Link className="nav-link" href="/options">
|
||||
Options
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<Link className="nav-link" href="/select">
|
||||
Select
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<button
|
||||
className="navbar-toggler border-0 ms-auto"
|
||||
type="button"
|
||||
data-bs-toggle="offcanvas"
|
||||
data-bs-target="#offcanvasNavbar"
|
||||
aria-controls="offcanvasNavbar"
|
||||
>
|
||||
<span className="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div
|
||||
className="offcanvas offcanvas-end"
|
||||
tabIndex={-1}
|
||||
id="offcanvasNavbar"
|
||||
aria-labelledby="offcanvasNavbarLabel"
|
||||
>
|
||||
<div className="offcanvas-header">
|
||||
<h5 className="offcanvas-title" id="offcanvasNavbarLabel">
|
||||
Menu
|
||||
</h5>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
data-bs-dismiss="offcanvas"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
<div className="offcanvas-body justify-content-center">
|
||||
<ul className="navbar-nav gap-lg-5">
|
||||
<li className="nav-item" data-bs-dismiss="offcanvas">
|
||||
<Link className="nav-link" href="/">
|
||||
Inicio
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item" data-bs-dismiss="offcanvas">
|
||||
<Link className="nav-link" href="/table">
|
||||
Table
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item" data-bs-dismiss="offcanvas">
|
||||
<Link className="nav-link" href="/input">
|
||||
Input
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item" data-bs-dismiss="offcanvas">
|
||||
<Link className="nav-link" href="/pagination">
|
||||
Pagination
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item" data-bs-dismiss="offcanvas">
|
||||
<Link className="nav-link" href="/options">
|
||||
Options
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item" data-bs-dismiss="offcanvas">
|
||||
<Link className="nav-link" href="/styles">
|
||||
Styles
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
"use client";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
export interface SidebarLink {
|
||||
icon?: string;
|
||||
text?: string;
|
||||
href?: string;
|
||||
show?: boolean;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
render?: () => React.ReactNode;
|
||||
}
|
||||
|
||||
interface SidebarProps {
|
||||
id: string;
|
||||
userName?: string;
|
||||
userRole?: string;
|
||||
logoSrc?: string;
|
||||
appTitle?: string;
|
||||
links: SidebarLink[];
|
||||
}
|
||||
|
||||
export default function Sidebar({
|
||||
userName = "Usuario",
|
||||
userRole = "Administrador",
|
||||
logoSrc = "/unam.png",
|
||||
appTitle = "Sistema de -------",
|
||||
links,
|
||||
id,
|
||||
}: SidebarProps) {
|
||||
const pathname = usePathname();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const open = localStorage.getItem(id) === "true";
|
||||
setIsOpen(open);
|
||||
}, [id]);
|
||||
|
||||
const toggleSidebar = () => {
|
||||
localStorage.setItem(id, `${!isOpen}`);
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<aside className={`sidebar ${isOpen ? "open" : "close"}`}>
|
||||
{/* Header for mobile */}
|
||||
<div className="d-flex justify-content-between align-items-center p-2 d-lg-none">
|
||||
<h1 className="h3 m-0">{appTitle}</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="m-0 p-0 border-0 bg-transparent"
|
||||
data-bs-toggle="offcanvas"
|
||||
data-bs-target={`#${id}-offcanvas`}
|
||||
aria-controls={`${id}-offcanvas`}
|
||||
>
|
||||
<i className="bi bi-list fs-2"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="offcanvas-lg offcanvas-end"
|
||||
tabIndex={-1}
|
||||
id={`${id}-offcanvas`}
|
||||
aria-labelledby={`${id}-offcanvas-label`}
|
||||
>
|
||||
<div className="offcanvas-header">
|
||||
<h5 className="offcanvas-title" id={`${id}-offcanvas-label`}>
|
||||
{appTitle}
|
||||
</h5>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
data-bs-dismiss="offcanvas"
|
||||
data-bs-target={`#${id}-offcanvas`}
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="offcanvas-body position-relative">
|
||||
{/* Toggle sidebar button */}
|
||||
<div
|
||||
className="d-none d-lg-flex position-absolute"
|
||||
style={{ top: "0", right: "-2rem" }}
|
||||
>
|
||||
<button
|
||||
className="bg-white border-0 shadow-sm px-2 py-1"
|
||||
type="button"
|
||||
onClick={toggleSidebar}
|
||||
>
|
||||
<i
|
||||
className={`bi bi-layout-sidebar-${
|
||||
isOpen ? "inset" : "inset-reverse"
|
||||
} text-muted`}
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{/* User Info */}
|
||||
<div className="d-flex align-items-center gap-3 my-2">
|
||||
<Image src={logoSrc} width={40} height={40} alt="Logo" />
|
||||
<div className="nav-link">
|
||||
<span>
|
||||
<b>{userName}</b>
|
||||
<br />
|
||||
<p className="text-muted m-0">{userRole}</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<ul className="nav flex-column gap-2">
|
||||
{links
|
||||
.filter((link) => link.show !== false)
|
||||
.map((link, index) => {
|
||||
if (link.render) {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{link.render()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const isActive =
|
||||
!!link.href &&
|
||||
(pathname === link.href ||
|
||||
(pathname?.startsWith(link.href) && link.href !== "/u"));
|
||||
|
||||
const baseClass = `nav-item ${isActive ? "active" : ""} ${
|
||||
link.className || ""
|
||||
}`;
|
||||
|
||||
return (
|
||||
<li
|
||||
key={index}
|
||||
className={baseClass}
|
||||
data-tooltip={link.text}
|
||||
data-bs-dismiss="offcanvas"
|
||||
data-bs-target="#offcanvasResponsive"
|
||||
aria-label="Close"
|
||||
>
|
||||
{link.href ? (
|
||||
<Link
|
||||
href={link.href}
|
||||
className="nav-link"
|
||||
onClick={link.onClick}
|
||||
>
|
||||
{link.icon && <i className={`bi ${link.icon}`}></i>}
|
||||
<span>{link.text}</span>
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
className="nav-link w-100 text-start"
|
||||
onClick={link.onClick}
|
||||
>
|
||||
{link.icon && <i className={`bi ${link.icon}`}></i>}
|
||||
<span>{link.text}</span>
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
.bg-gradient-#{$color-name}-bottom {
|
||||
background-image: linear-gradient(
|
||||
to bottom,
|
||||
rgba($color-value, 0.3),
|
||||
rgba($color-value, 0.5),
|
||||
$body-bg
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Clases personalizadas de hover por color
|
||||
@each $name, $color in $theme-colors {
|
||||
.hover-bg-#{$name} {
|
||||
transition: background-color 0.4s;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background-color: rgba($color, 0.75) !important;
|
||||
.nav-link {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
.sidebar {
|
||||
@extend .py-0;
|
||||
@extend .box;
|
||||
@extend .m-lg-2;
|
||||
|
||||
.nav-item {
|
||||
@extend .rounded-pill;
|
||||
@extend .w-100;
|
||||
transition: background-color 0.4s;
|
||||
|
||||
// Hover por defecto (solo si no tiene clase hover-bg-*)
|
||||
&:not([class*="hover-bg-"]):hover,
|
||||
&:not([class*="hover-bg-"]).active {
|
||||
@extend .bg-primary;
|
||||
@extend .bg-opacity-75;
|
||||
|
||||
.nav-link {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
i {
|
||||
@extend .me-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
@extend .py-lg-2;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: clamp(250px, 100%, 300px);
|
||||
max-width: 300px;
|
||||
transition: width 0.3s ease;
|
||||
|
||||
.offcanvas-body {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.open {
|
||||
.nav-link {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
span {
|
||||
transition: opacity 0.3s, visibility 0.3s;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.close {
|
||||
width: 80px;
|
||||
|
||||
.nav-item:hover {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: attr(data-tooltip);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 100%;
|
||||
transform: translate(10px, -50%);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
opacity: 0;
|
||||
animation: fadeInOut 4s ease-in-out;
|
||||
font-style: normal;
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
span {
|
||||
transition: opacity 0.3s, visibility 0.3s;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+16
-12
@@ -16,31 +16,35 @@ $custom-colors: (
|
||||
|
||||
$theme-colors: map-merge($theme-colors, $custom-colors);
|
||||
|
||||
@import "gradients";
|
||||
@import "extends";
|
||||
@import "table";
|
||||
@import "utilities";
|
||||
|
||||
@import "bootstrap/scss/maps";
|
||||
@import "bootstrap/scss/mixins";
|
||||
@import "bootstrap/scss/bootstrap";
|
||||
|
||||
@import "gradients";
|
||||
@import "extends";
|
||||
@import "table";
|
||||
@import "utilities";
|
||||
@import 'sidebar';
|
||||
@import 'hover';
|
||||
|
||||
//Add additional custom code here
|
||||
|
||||
@media (min-width: 992px) {
|
||||
@include media-breakpoint-up(lg) {
|
||||
.bd-layout {
|
||||
display: grid;
|
||||
grid-template-areas: "sidebar main";
|
||||
grid-template-columns: 1fr 5fr;
|
||||
gap: 1.5rem;
|
||||
gap: $spacer * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.bd-sidebar {
|
||||
grid-area: sidebar;
|
||||
position: sticky;
|
||||
top: ($spacer * 3);
|
||||
height: calc(100vh - 6rem);
|
||||
@include media-breakpoint-up(lg) {
|
||||
.bd-sidebar {
|
||||
grid-area: sidebar;
|
||||
position: sticky;
|
||||
top: ($spacer * 3);
|
||||
height: calc(100vh - 6rem);
|
||||
}
|
||||
}
|
||||
|
||||
ul.list-unstyled li {
|
||||
|
||||
Reference in New Issue
Block a user