feat: Add ProgressBar component with skeleton status and documentation

- Implemented ProgressBar component to display progress based on states.
- Added EstatusEskeleton for loading state indication.
- Created documentation page for ProgressBar with examples and props table.
- Developed reusable CodeBlock and VistaComponente components for code display.
- Introduced IndiceContenido for navigation within documentation.
- Styled ProgressBar and related components with SCSS.
This commit is contained in:
jalvarado
2025-05-29 10:37:10 -06:00
parent 0195bf0778
commit 3e64837dd3
23 changed files with 681 additions and 377 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+2 -2
View File
@@ -1,7 +1,7 @@
'use client';
import IndiceContenido from '@/components/content-index';
import VistaComponente from '@/components/vista-componente';
import IndiceContenido from '@/components/docs/content-index';
import VistaComponente from '@/components/docs/vista-componente';
import Button from '@/components/button';
export default function Page() {
+2 -2
View File
@@ -1,9 +1,9 @@
'use client';
import IndiceContenido from '@/components/content-index';
import IndiceContenido from '@/components/docs/content-index';
import Input from '@/components/input';
import PasswordInput from '@/components/password';
import VistaComponente from '@/components/vista-componente';
import VistaComponente from '@/components/docs/vista-componente';
import {
inputBasicExample,
inputPasswordExample,
+51 -51
View File
@@ -1,40 +1,40 @@
"use client";
'use client';
import React, { useState } from "react";
import RadioOptionGroup, { RadioOption } from "@/components/radio-option-group";
import React, { useState } from 'react';
import RadioOptionGroup, { RadioOption } from '@/components/radio-option-group';
import CheckboxOptionGroup, {
CheckboxOption,
} from "@/components/checkbox-option-group";
import VistaComponente from "@/components/vista-componente";
import { CodeBlock } from "@/components/code-block";
} from '@/components/checkbox-option-group';
import VistaComponente from '@/components/docs/vista-componente';
import { CodeBlock } from '@/components/docs/code-block';
import {
checkboxBasicExample,
checkboxLimitExample,
radioBasicExample,
radioDisabledExample,
} from "@/snippets/options/option-examples";
import IndiceContenido from "@/components/content-index";
} from '@/snippets/options/option-examples';
import IndiceContenido from '@/components/docs/content-index';
export default function RadioCheckboxDocs() {
const [selectedRadio, setSelectedRadio] = useState("op1");
const [selectedRadio, setSelectedRadio] = useState('op1');
const [checkboxBasicSelected, setCheckboxBasicSelected] = useState<string[]>([
"a",
'a',
]);
const [checkboxLimitSelected, setCheckboxLimitSelected] = useState<string[]>(
[]
);
const radioOptions: RadioOption<string>[] = [
{ label: "Opción 1", value: "op1" },
{ label: "Opción 2", value: "op2" },
{ label: "Opción 3", value: "op3" },
{ label: 'Opción 1', value: 'op1' },
{ label: 'Opción 2', value: 'op2' },
{ label: 'Opción 3', value: 'op3' },
];
const checkboxOptions: CheckboxOption<string>[] = [
{ label: "Activo", value: "a" },
{ label: "Inactivo", value: "b" },
{ label: "Pendiente", value: "c" },
{ label: 'Activo', value: 'a' },
{ label: 'Inactivo', value: 'b' },
{ label: 'Pendiente', value: 'c' },
];
const handleCheckboxChange = (
@@ -51,24 +51,24 @@ export default function RadioCheckboxDocs() {
<>
<IndiceContenido
items={[
{ id: "uso-basico", label: "Uso básico" },
{ id: "opcion-deshabilitada", label: "Opción deshabilitada" },
{ id: "checkbox-basico", label: "Checkbox: Uso básico" },
{ id: "checkbox-limite", label: "Checkbox: Límite de selección" },
{ id: 'uso-basico', label: 'Uso básico' },
{ id: 'opcion-deshabilitada', label: 'Opción deshabilitada' },
{ id: 'checkbox-basico', label: 'Checkbox: Uso básico' },
{ id: 'checkbox-limite', label: 'Checkbox: Límite de selección' },
]}
/>
<div className="my-5">
<h1 className="mb-4 display-5">
Componentes: <code>RadioOptionGroup</code> y{" "}
<div className='my-5'>
<h1 className='mb-4 display-5'>
Componentes: <code>RadioOptionGroup</code> y{' '}
<code>CheckboxOptionGroup</code>
</h1>
<p className="mb-4 text-muted">
<p className='mb-4 text-muted'>
Estos componentes permiten mostrar grupos de opciones radio o
checkbox, para seleccionar una o varias opciones respectivamente. Son
útiles en formularios, encuestas o filtros. Los estilos de estos
componentes se basan en Bootstrap y se encuentran el el archivo{" "}
<code>@/styles/sass/_extneds.scss</code> y es el selector{" "}
componentes se basan en Bootstrap y se encuentran el el archivo{' '}
<code>@/styles/sass/_extneds.scss</code> y es el selector{' '}
<code>.option</code>.
</p>
@@ -77,16 +77,16 @@ export default function RadioCheckboxDocs() {
import CheckboxOptionGroup, { CheckboxOption } from "@/components/checkbox-option-group";`}
/>
<hr className="my-5" />
<hr className='my-5' />
<VistaComponente
title="Radio: Uso básico"
description="Grupo de opciones radio controladas por estado."
title='Radio: Uso básico'
description='Grupo de opciones radio controladas por estado.'
code={radioBasicExample}
id="uso-basico"
id='uso-basico'
>
<RadioOptionGroup
name="grupo-radio"
name='grupo-radio'
options={radioOptions}
selectedValue={selectedRadio}
onChange={setSelectedRadio}
@@ -94,30 +94,30 @@ import CheckboxOptionGroup, { CheckboxOption } from "@/components/checkbox-optio
</VistaComponente>
<VistaComponente
title="Radio: Opción deshabilitada"
description="Se pueden deshabilitar opciones individualmente."
title='Radio: Opción deshabilitada'
description='Se pueden deshabilitar opciones individualmente.'
code={radioDisabledExample}
id="opcion-deshabilitada"
id='opcion-deshabilitada'
>
<RadioOptionGroup
name="grupo-radio-disabled"
name='grupo-radio-disabled'
options={[
{ label: "Activo", value: "a" },
{ label: "Inactivo", value: "b", disabled: true },
{ label: 'Activo', value: 'a' },
{ label: 'Inactivo', value: 'b', disabled: true },
]}
selectedValue="a"
selectedValue='a'
onChange={() => {}}
/>
</VistaComponente>
<VistaComponente
title="Checkbox: Uso básico"
description="Grupo de opciones múltiples con control de estado."
title='Checkbox: Uso básico'
description='Grupo de opciones múltiples con control de estado.'
code={checkboxBasicExample}
id="checkbox-basico"
id='checkbox-basico'
>
<CheckboxOptionGroup
name="grupo-checkbox"
name='grupo-checkbox'
options={checkboxOptions}
selectedValues={checkboxBasicSelected}
onChange={(value, checked) =>
@@ -127,13 +127,13 @@ import CheckboxOptionGroup, { CheckboxOption } from "@/components/checkbox-optio
</VistaComponente>
<VistaComponente
title="Checkbox: Límite de selección"
description="Usa `maxSelected` para limitar la cantidad de opciones seleccionables."
title='Checkbox: Límite de selección'
description='Usa `maxSelected` para limitar la cantidad de opciones seleccionables.'
code={checkboxLimitExample}
id="checkbox-limite"
id='checkbox-limite'
>
<CheckboxOptionGroup
name="grupo-checkbox-limit"
name='grupo-checkbox-limit'
options={checkboxOptions}
selectedValues={checkboxLimitSelected}
onChange={(value, checked) =>
@@ -143,14 +143,14 @@ import CheckboxOptionGroup, { CheckboxOption } from "@/components/checkbox-optio
/>
</VistaComponente>
<section className="mb-5 mt-5">
<h2 className="h5">Props comunes</h2>
<section className='mb-5 mt-5'>
<h2 className='h5'>Props comunes</h2>
<ul>
<li>
<code>name</code>: Nombre del grupo de inputs.
</li>
<li>
<code>options</code>: Lista de opciones con <code>label</code>,{" "}
<code>options</code>: Lista de opciones con <code>label</code>,{' '}
<code>value</code> y opcionalmente <code>disabled</code>.
</li>
<li>
@@ -158,7 +158,7 @@ import CheckboxOptionGroup, { CheckboxOption } from "@/components/checkbox-optio
</li>
</ul>
<h2 className="h5 mt-4">
<h2 className='h5 mt-4'>
Props de <code>RadioOptionGroup</code>
</h2>
<ul>
@@ -174,7 +174,7 @@ import CheckboxOptionGroup, { CheckboxOption } from "@/components/checkbox-optio
</li>
</ul>
<h2 className="h5 mt-4">
<h2 className='h5 mt-4'>
Props de <code>CheckboxOptionGroup</code>
</h2>
<ul>
+25 -25
View File
@@ -1,14 +1,14 @@
"use client";
'use client';
import React, { useState } from "react";
import VistaComponente from "@/components/vista-componente";
import { CodeBlock } from "@/components/code-block";
import Pagination from "@/components/pagination";
import React, { useState } from 'react';
import VistaComponente from '@/components/docs/vista-componente';
import { CodeBlock } from '@/components/docs/code-block';
import Pagination from '@/components/pagination';
import {
paginationBasic,
paginationWithDelta,
} from "@/snippets/pagination/pagination-example";
import IndiceContenido from "@/components/content-index";
} from '@/snippets/pagination/pagination-example';
import IndiceContenido from '@/components/docs/content-index';
export default function Page() {
const [currentPage1, setCurrentPage1] = useState(1);
@@ -18,20 +18,20 @@ export default function Page() {
<>
<IndiceContenido
items={[
{ id: "uso-basico", label: "Uso básico" },
{ id: 'uso-basico', label: 'Uso básico' },
{
id: "control-con-delta",
label: "Control de visibilidad con `delta`",
id: 'control-con-delta',
label: 'Control de visibilidad con `delta`',
},
{ id: "pagination-props", label: "Props del componente" },
{ id: 'pagination-props', label: 'Props del componente' },
]}
/>
<div className="my-5">
<h1 className="mb-4 display-5">
<div className='my-5'>
<h1 className='mb-4 display-5'>
📄 Componente: <code>Pagination</code>
</h1>
<p className="mb-4 text-muted">
<p className='mb-4 text-muted'>
El componente <code>&lt;Pagination /&gt;</code> permite navegar entre
páginas de forma controlada. Es completamente reutilizable y permite
mostrar botones de anterior/siguiente y paginación con saltos.
@@ -39,31 +39,31 @@ export default function Page() {
<CodeBlock code={`import Pagination from "@/components/pagination";`} />
<hr className="my-5" />
<hr className='my-5' />
{/* Ejemplo básico */}
<VistaComponente
title="Uso básico"
description="Muestra los botones de página y permite cambiar de página externamente con `onPageChange`."
title='Uso básico'
description='Muestra los botones de página y permite cambiar de página externamente con `onPageChange`.'
code={paginationBasic}
id="uso-basico"
id='uso-basico'
>
<Pagination
totalPages={5}
currentPage={currentPage1}
onPageChange={setCurrentPage1}
/>
<p className="mt-2 text-sm text-muted">
<p className='mt-2 text-sm text-muted'>
Página actual: {currentPage1}
</p>
</VistaComponente>
{/* Ejemplo con delta */}
<VistaComponente
title="🔍 Control de visibilidad con `delta`"
description="Usa la prop `delta` para definir cuántas páginas cercanas a la actual deben mostrarse."
title='🔍 Control de visibilidad con `delta`'
description='Usa la prop `delta` para definir cuántas páginas cercanas a la actual deben mostrarse.'
code={paginationWithDelta}
id="control-con-delta"
id='control-con-delta'
>
<Pagination
totalPages={20}
@@ -71,14 +71,14 @@ export default function Page() {
onPageChange={setCurrentPage2}
delta={2}
/>
<p className="mt-2 text-sm text-muted">
<p className='mt-2 text-sm text-muted'>
Página actual: {currentPage2}
</p>
</VistaComponente>
{/* Props */}
<section className="mb-5 mt-5">
<h2 className="h5"> Props del componente</h2>
<section className='mb-5 mt-5'>
<h2 className='h5'> Props del componente</h2>
<ul>
<li>
<code>totalPages</code>: <em>number</em> Total de páginas
+2 -2
View File
@@ -1,7 +1,7 @@
'use client';
import IndiceContenido from '@/components/content-index';
import VistaComponente from '@/components/vista-componente';
import IndiceContenido from '@/components/docs/content-index';
import VistaComponente from '@/components/docs/vista-componente';
import Popover from '@/components/popover';
export default function Page() {
+102
View File
@@ -0,0 +1,102 @@
'use client';
import { CodeBlock } from '@/components/docs/code-block';
import IndiceContenido from '@/components/docs/content-index';
import VistaComponente from '@/components/docs/vista-componente';
import ProgressBar, { EstatusEskeleton } from '@/components/progress-bar';
export default function Page() {
const estados = ['Pendiente', 'En proceso', 'Completado', 'Entregado'];
return (
<>
<IndiceContenido
items={[
{ id: 'bar-progreso', label: 'Barra de progreso' },
{ id: 'skeleton', label: 'Estatus Skeleton' },
{ id: 'props', label: 'Props del componente' },
]}
/>
<div className='my-5'>
<h1 className='mb-4 display-5'>
🚀 Componente: <code>ProgressBar</code>
</h1>
<p className='mb-4 text-muted'>
El componente <code>&lt;ProgressBar /&gt;</code> muestra una barra de
progreso basada en una lista de estados y el índice del estado activo.
Incluye vista responsiva: multistep en pantallas md+ y paso único en
móvil.
</p>
<CodeBlock
code={`import ProgressBar from "@/components/progress-bar";`}
/>
<hr className='my-5' />
<VistaComponente
id='bar-progreso'
title='📈 Barra de progreso'
description='Muestra todos los pasos con indicador de activo.'
code={`<ProgressBar
estados={["Paso 1", "Paso 2", "Paso 3"]}
estadoActivo={1}
/>`}
>
<ProgressBar estados={estados} estadoActivo={2} />
</VistaComponente>
<VistaComponente
id='skeleton'
title='💤 Estatus Skeleton'
description='Placeholder mientras se carga el estado.'
code={`<EstatusEskeleton />`}
>
<EstatusEskeleton />
</VistaComponente>
{/* PROPS */}
<section className='mb-5' id='props'>
<h2 className='h5 mb-3'>
Props de <code>ProgressBar</code>
</h2>
<table className='table table-bordered table-striped align-middle'>
<thead>
<tr>
<th>Prop</th>
<th>Tipo</th>
<th>Descripción</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>estados</code>
</td>
<td>
<code>string[]</code>
</td>
<td>Listado de etiquetas para cada paso.</td>
</tr>
<tr>
<td>
<code>estadoActivo</code>
</td>
<td>
<code>number</code>
</td>
<td>Índice (0-based) del paso actualmente activo.</td>
</tr>
</tbody>
</table>
<h2 className='h5 mt-4 mb-3'>
Props de <code>EstatusEskeleton</code>
</h2>
<p className='text-muted'>Este componente no recibe props.</p>
</section>
</div>
</>
);
}
+43 -40
View File
@@ -1,52 +1,55 @@
"use client";
'use client';
import IndiceContenido from "@/components/content-index";
import Select from "@/components/select";
import VistaComponente from "@/components/vista-componente";
import { countrySelectSnippet, languageSelectSnippet } from "@/snippets/select/select";
import { useState } from "react";
import IndiceContenido from '@/components/docs/content-index';
import Select from '@/components/select';
import VistaComponente from '@/components/docs/vista-componente';
import {
countrySelectSnippet,
languageSelectSnippet,
} from '@/snippets/select/select';
import { useState } from 'react';
export default function Page() {
const [pais, setPais] = useState("");
const [pais, setPais] = useState('');
const opcionesPais = [
{ value: "mx", label: "México" },
{ value: "us", label: "Estados Unidos" },
{ value: "ca", label: "Canadá" },
{ value: 'mx', label: 'México' },
{ value: 'us', label: 'Estados Unidos' },
{ value: 'ca', label: 'Canadá' },
];
return (
<>
<IndiceContenido
items={[
{ id: "select-basico", label: "Select básico" },
{ id: "select-estilos", label: "Personalización de estilos" },
{ id: "select-props", label: "Props del componente" },
{ id: 'select-basico', label: 'Select básico' },
{ id: 'select-estilos', label: 'Personalización de estilos' },
{ id: 'select-props', label: 'Props del componente' },
]}
/>
<div className="my-5">
<h1 className="mb-4 display-5">
<div className='my-5'>
<h1 className='mb-4 display-5'>
Componente: <code>Select</code>
</h1>
<p className="mb-4 text-muted">
<p className='mb-4 text-muted'>
El componente <code>&lt;Select /&gt;</code> permite mostrar una lista
desplegable con opciones personalizables, soporta etiquetas, estilos
por clase, y es compatible con formularios.
</p>
<hr className="my-5" />
<hr className='my-5' />
<VistaComponente
title="Select básico"
description="Un select controlado con opciones simples y un placeholder deshabilitado."
title='Select básico'
description='Un select controlado con opciones simples y un placeholder deshabilitado.'
code={countrySelectSnippet}
id="select-basico"
id='select-basico'
>
<Select
label="País"
name="pais"
placeholder="Selecciona un país"
label='País'
name='pais'
placeholder='Selecciona un país'
options={opcionesPais}
value={pais}
onChange={(e) => setPais(e.target.value)}
@@ -54,30 +57,30 @@ export default function Page() {
</VistaComponente>
<VistaComponente
title="Personalización de estilos"
description="Puedes aplicar clases personalizadas al contenedor, etiqueta o al select."
title='Personalización de estilos'
description='Puedes aplicar clases personalizadas al contenedor, etiqueta o al select.'
code={languageSelectSnippet}
id="select-estilos"
id='select-estilos'
>
<Select
label="Idioma"
name="idioma"
placeholder="Selecciona un idioma"
label='Idioma'
name='idioma'
placeholder='Selecciona un idioma'
options={[
{ value: "es", label: "Español" },
{ value: "en", label: "Inglés" },
{ value: "fr", label: "Francés" },
{ value: 'es', label: 'Español' },
{ value: 'en', label: 'Inglés' },
{ value: 'fr', label: 'Francés' },
]}
className={{
container: "mb-4",
label: "text-primary fw-bold",
input: "border border-success",
container: 'mb-4',
label: 'text-primary fw-bold',
input: 'border border-success',
}}
/>
</VistaComponente>
<section className="mb-5">
<h2 className="h5">Props del componente</h2>
<section className='mb-5'>
<h2 className='h5'>Props del componente</h2>
<ul>
<li>
<code>label: string</code> Etiqueta visible del campo.
@@ -86,7 +89,7 @@ export default function Page() {
<code>
options: &#123; value: string &#124; number; label: string
&#125;[]
</code>{" "}
</code>{' '}
Opciones a mostrar.
</li>
<li>
@@ -108,11 +111,11 @@ export default function Page() {
(modo controlado).
</li>
<li>
<code>className?: &#123; container?, label?, input? &#125;</code>{" "}
<code>className?: &#123; container?, label?, input? &#125;</code>{' '}
Clases personalizadas.
</li>
<li>
<code>...rest</code> Cualquier otra prop válida de{" "}
<code>...rest</code> Cualquier otra prop válida de{' '}
<code>&lt;select&gt;</code>.
</li>
</ul>
+96 -98
View File
@@ -1,64 +1,64 @@
"use client";
import IndiceContenido from "@/components/content-index";
import Sidebar, { SidebarLink } from "@/components/sidebar";
import VistaComponente from "@/components/vista-componente";
import React from "react";
'use client';
import IndiceContenido from '@/components/docs/content-index';
import Sidebar, { SidebarLink } from '@/components/sidebar';
import VistaComponente from '@/components/docs/vista-componente';
import React from 'react';
const basicLinks: SidebarLink[] = [
{ icon: "bi-house", text: "Inicio", href: "/inicio" },
{ icon: 'bi-house', text: 'Inicio', href: '/inicio' },
{
icon: "bi-person",
text: "Perfil",
href: "/perfil",
className: "hover-bg-success",
icon: 'bi-person',
text: 'Perfil',
href: '/perfil',
className: 'hover-bg-success',
},
{
icon: "bi-envelope",
text: "Mensajes",
href: "/mensajes",
className: "hover-bg-info",
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-gear',
text: 'Configuración',
href: '/configuracion',
className: 'hover-bg-warning',
},
{
icon: "bi-bar-chart",
text: "Reportes",
href: "/reportes",
className: "hover-bg-danger",
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-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-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",
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>
<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>
@@ -66,9 +66,9 @@ const renderItems: SidebarLink[] = [
},
{
render: () => (
<li className="nav-item">
<div className="nav-link text-primary">
<i className="bi bi-heart-fill me-2"></i>
<li className='nav-item'>
<div className='nav-link text-primary'>
<i className='bi bi-heart-fill me-2'></i>
<span>Favoritos</span>
</div>
</li>
@@ -76,9 +76,9 @@ const renderItems: SidebarLink[] = [
},
{
render: () => (
<li className="nav-item hover-bg-danger">
<div className="nav-link text-danger">
<i className="bi bi-exclamation-circle-fill me-2"></i>
<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>
@@ -86,9 +86,9 @@ const renderItems: SidebarLink[] = [
},
{
render: () => (
<li className="nav-item">
<div className="nav-link text-warning">
<i className="bi bi-lightbulb-fill me-2"></i>
<li className='nav-item'>
<div className='nav-link text-warning'>
<i className='bi bi-lightbulb-fill me-2'></i>
<span>Ideas</span>
</div>
</li>
@@ -101,25 +101,25 @@ export default function Page() {
<>
<IndiceContenido
items={[
{ id: "sidebar-ejemplo", label: "Sidebar básico" },
{ id: "sidebar-acciones", label: "Sidebar con acciones" },
{ 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-render-items',
label: 'Sidebar con elementos personalizados',
},
{
id: "sidebar-colores",
label: "Sidebar con personalización de colores",
id: 'sidebar-colores',
label: 'Sidebar con personalización de colores',
},
]}
/>
<div className="my-5">
<h1 className="mb-4 display-5">
<div className='my-5'>
<h1 className='mb-4 display-5'>
Componente: <code>Sidebar</code>
</h1>
<p className="mb-4 text-muted">
<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
@@ -127,43 +127,41 @@ export default function Page() {
elementos renderizados dinámicamente.
</p>
<hr className="my-5"/>
<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"
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">
<div className='d-lg-flex'>
<Sidebar
id="sidebar"
userName="Usuario"
userRole="Administrador"
appTitle="Sistema de Encuestas"
logoSrc="/unam.png"
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>
<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"
title='Sidebar con acciones'
description='Menú lateral con enlaces que ejecutan funciones al hacer clic.'
id='sidebar-acciones'
>
<div className="d-lg-flex">
<div className='d-lg-flex'>
<Sidebar
id="sidebar-acciones"
userName="Usuario"
userRole="Administrador"
appTitle="Sistema de Encuestas"
logoSrc="/unam.png"
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">
<main className='flex-grow-1 p-4 border rounded'>
<h1>Contenido interactivo</h1>
<p>
Cada enlace ejecuta una acción mediante <code>onClick</code>.
@@ -175,20 +173,20 @@ export default function Page() {
</VistaComponente>
<VistaComponente
title="Sidebar con elementos personalizados"
description="Uso de la propiedad render para incluir componentes únicos dentro del menú."
id="sidebar-render-items"
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">
<div className='d-lg-flex'>
<Sidebar
id="sidebar-render-items"
userName="Usuario"
userRole="Administrador"
appTitle="Sistema de Encuestas"
logoSrc="/unam.png"
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">
<main className='flex-grow-1 p-4 border rounded'>
<h1>Elementos renderizados</h1>
<p>
La propiedad <code>render</code> permite insertar cualquier JSX
@@ -200,20 +198,20 @@ export default function Page() {
</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"
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">
<div className='d-lg-flex'>
<Sidebar
id="sidebar-colores"
userName="Usuario"
userRole="Administrador"
appTitle="Sistema de Encuestas"
logoSrc="/unam.png"
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">
<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
+58 -56
View File
@@ -1,123 +1,125 @@
"use client";
import React from "react";
import IndiceContenido from "@/components/content-index";
import VistaComponente from "@/components/vista-componente";
'use client';
import React from 'react';
import IndiceContenido from '@/components/docs/content-index';
import VistaComponente from '@/components/docs/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" },
{ 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>
<div className='my-5'>
<h1 className='mb-4 display-5'>Estilos</h1>
<p className="mb-4 text-muted">
<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" />
<hr className='my-5' />
<div className="alert alert-info">
<i className="bi bi-info-circle me-2"></i>
<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."
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">
<div className='box mb-3'>
<h1 className='mb-0'>
<code>div</code> con className <code>.box</code>
</h1>
</div>
</VistaComponente>
<VistaComponente
id="box"
title="Estilo: Box activado"
description="El estilo normal de la caja se puede activar con la clase <code>.active</code> para resaltar un elemento seleccionado o activo."
id='box'
title='Estilo: Box activado'
description='El estilo normal de la caja se puede activar con la clase <code>.active</code> para resaltar un elemento seleccionado o activo.'
code={`<div className="box active">
<h1><code>div</code> con className <code>.box</code></h1>
</div>`}
>
<div className="box active mb-3">
<h1 className="mb-0">
<code>div</code> con className <code>.box</code> <code>.active</code>
<div className='box active mb-3'>
<h1 className='mb-0'>
<code>div</code> con className <code>.box</code>{' '}
<code>.active</code>
</h1>
</div>
</VistaComponente>
<VistaComponente
id="box"
title="Estilo: Box desactivado"
description="El estilo normal de la caja se puede desactivar con la clase <code>.disabled</code> para indicar que un elemento no está disponible o no se puede seleccionar."
id='box'
title='Estilo: Box desactivado'
description='El estilo normal de la caja se puede desactivar con la clase <code>.disabled</code> para indicar que un elemento no está disponible o no se puede seleccionar.'
code={`<div className="box disabled">
<h1><code>div</code> con className <code>.box</code></h1>
</div>`}
>
<div className="box disabled mb-3">
<h1 className="mb-0">
<code>div</code> con className <code>.box</code> <code>.disabled</code>
<div className='box disabled mb-3'>
<h1 className='mb-0'>
<code>div</code> con className <code>.box</code>{' '}
<code>.disabled</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"
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">
<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">
<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">
<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">
<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">
<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">
<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."
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">
<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" }}
style={{ width: '150px' }}
>
<strong>{`scale-hover-${scale}`}</strong>
<br />({(1 + scale * 0.05).toFixed(2)}x)
@@ -127,33 +129,33 @@ export default function Page() {
</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."
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(
<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" }}
style={{ width: '180px' }}
>
<strong>{`hover-bg-${color}`}</strong>
<br />
{color === "primary" && "(hover o active)"}
{color === 'primary' && '(hover o active)'}
</div>
)
)}
</div>
<h2 className="mt-4 h6">
<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="#">
<div className='hover-bg-primary p-3 rounded mb-3'>
<a className='nav-link' href='#'>
Enlace dentro de hover-bg-primary
</a>
</div>
+3 -3
View File
@@ -2,7 +2,7 @@
import React, { useState } from 'react';
import Table, { Header, TableSkeleton } from '@/components/table';
import VistaComponente from '@/components/vista-componente';
import VistaComponente from '@/components/docs/vista-componente';
import {
tableBasicExample,
tableSkeletonExample,
@@ -12,10 +12,10 @@ import { tableAutoPagination } from '@/snippets/table/table-auto-pagination';
import { tableCustomPagination } from '@/snippets/table/table-custom-pagination';
import { tableCustomCellRender } from '@/snippets/table/table-custom-cell-render';
import { tableColumnWidth } from '@/snippets/table/table-column-width';
import { CodeBlock } from '@/components/code-block';
import { CodeBlock } from '@/components/docs/code-block';
import Input from '@/components/input';
import { tableFilter } from '@/snippets/table/table-filter';
import IndiceContenido from '@/components/content-index';
import IndiceContenido from '@/components/docs/content-index';
export default function Page() {
const headers: Header<{
+155 -77
View File
@@ -1,44 +1,74 @@
"use client";
'use client';
import { CodeBlock } from "@/components/code-block";
import IndiceContenido from "@/components/content-index";
import Tabs from "@/components/tabs";
import VistaComponente from "@/components/vista-componente";
import { CodeBlock } from '@/components/docs/code-block';
import IndiceContenido from '@/components/docs/content-index';
import Tabs from '@/components/tabs';
import VistaComponente from '@/components/docs/vista-componente';
export default function Page() {
return (
<>
<IndiceContenido
items={[
{ id: "tabs-basico", label: "Pestañas básicas" },
{ id: "tabs-desactivadas", label: "Pestañas desactivadas" },
{ id: "tabs-estilos", label: "Estilos del componente" },
{ id: "tabs-props", label: "Props del componente" },
{ id: 'tabs-basico', label: 'Pestañas básicas' },
{ id: 'tabs-desactivadas', label: 'Pestañas desactivadas' },
{ id: 'tabs-estilos', label: 'Estilos del componente' },
{ id: 'tabs-default', label: 'Pestaña activa por defecto' },
{ id: 'tabs-props', label: 'Props del componente' },
]}
/>
<div className="my-5">
<h1 className="mb-4 display-5">
<div className='my-5'>
<h1 className='mb-4 display-5'>
🧩 Componente: <code>Tabs</code>
</h1>
<p className="mb-4 text-muted">
El componente <code>&lt;Tabs /&gt;</code> permite organizar contenido
en pestañas, ofreciendo opciones de estilos y soporte para pestañas
desactivadas.
<br />
El tiempo de animacion de las pestañas es de 0.3s. pero se puede
modificar en el archivo <code>src/styles/sass/_tabs.scss</code>
<div style={{ margin: '150px' }}>
<Tabs
defaultActiveTab='profile-4'
tabs={[
{ id: 'home-4', title: 'Inicio', content: <p>Inicio</p> },
{ id: 'profile-4', title: 'Perfil', content: <p>Perfil</p> },
{
id: 'settings-4',
title: 'Configuraciones',
content: <p>Configuraciones</p>,
},
]}
/>
<Tabs
style='box'
tabs={[
{
id: 'home-3',
title: 'Inicio',
content: <p>Contenido de inicio</p>,
},
{
id: 'profile-3',
title: 'Perfil',
content: <p>Contenido del perfil</p>,
},
]}
/>
</div>
<p className='mb-4 text-muted'>
El componente <code>&lt;Tabs /&gt;</code> organiza contenido en
pestañas, con opciones de estilo (<code>default</code> y{' '}
<code>box</code>), soporte para pestañas deshabilitadas y pestaña
activa inicial via
<code>defaultActiveTab</code>.
</p>
<CodeBlock code={`import Tabs from "@/components/tabs";`} />
<hr className="my-5" />
<hr className='my-5' />
<VistaComponente
id="tabs-basico"
title="📌 Pestañas básicas"
description="Un conjunto de pestañas funcionales para organizar contenido."
id='tabs-basico'
title='📌 Pestañas básicas'
description='Un conjunto de pestañas funcionales para organizar contenido.'
code={`<Tabs
tabs=[
{ id: "home", title: "Inicio", content: <p>Contenido de inicio</p> },
@@ -50,45 +80,45 @@ export default function Page() {
<Tabs
tabs={[
{
id: "home-1",
title: "Inicio",
id: 'home-1',
title: 'Inicio',
content: <p>Contenido de inicio</p>,
},
{
id: "profile-1",
title: "Perfil",
id: 'profile-1',
title: 'Perfil',
content: <p>Contenido del perfil</p>,
},
{
id: "settings-1",
title: "Configuraciones",
content: <p>Contenido de configuraciones</p>,
id: 'settings-1',
title: 'Configuraciones',
content: <p>Configuraciones</p>,
},
]}
/>
</VistaComponente>
<VistaComponente
id="tabs-desactivadas"
title="🚫 Pestañas desactivadas"
description="Puedes desactivar pestañas para evitar que sean seleccionadas."
id='tabs-desactivadas'
title='🚫 Pestañas desactivadas'
description='Puedes desactivar pestañas para evitar que sean seleccionadas.'
code={`<Tabs
tabs=[
{ id: "home", title: "Inicio", content: <p>Contenido de inicio</p> },
{ id: "disabled", title: "Desactivado", content: <p>No seleccionable</p>, disabled: true },
{ id: "home", title: "Inicio", content: <p>...</p> },
{ id: "disabled", title: "Desactivado", content: <p>...</p>, disabled: true },
]
/>`}
>
<Tabs
tabs={[
{
id: "home-2",
title: "Inicio",
id: 'home-2',
title: 'Inicio',
content: <p>Contenido de inicio</p>,
},
{
id: "disabled-2",
title: "Desactivado",
id: 'disabled-2',
title: 'Desactivado',
content: <p>No seleccionable</p>,
disabled: true,
},
@@ -97,49 +127,64 @@ export default function Page() {
</VistaComponente>
<VistaComponente
id="tabs-estilos"
title="🎨 Estilos del componente"
description="El componente admite diferentes estilos, como el diseño en cajas."
id='tabs-estilos'
title='🎨 Estilos del componente'
description="Admite estilos 'default' y 'box'."
code={`<Tabs
tabs=[
{ id: "home", title: "Inicio", content: <p>Contenido de inicio</p> },
{ id: "profile", title: "Perfil", content: <p>Contenido del perfil</p> },
]
style="box"
tabs=[
{ id: "home", title: "Inicio", content: <p>...</p> },
{ id: "profile", title: "Perfil", content: <p>...</p> },
]
/>`}
>
<Tabs
style="box"
style='box'
tabs={[
{
id: "home-3",
title: "Inicio",
id: 'home-3',
title: 'Inicio',
content: <p>Contenido de inicio</p>,
},
{
id: "profile-3",
title: "Perfil",
id: 'profile-3',
title: 'Perfil',
content: <p>Contenido del perfil</p>,
},
{
id: "settings-3",
title: "Configuraciones",
content: <p>Contenido del perfil</p>,
disabled: true,
},
]}
/>
</VistaComponente>
<VistaComponente
id='tabs-default'
title='✨ Pestaña activa por defecto'
description='Utiliza <code>defaultActiveTab</code> para indicar la pestaña inicial.'
code={`<Tabs
defaultActiveTab="profile"
tabs=[
{ id: "home", title: "Inicio", content: <p>...</p> },
{ id: "profile", title: "Perfil", content: <p>...</p> },
]
/>`}
>
<Tabs
defaultActiveTab='profile-4'
tabs={[
{ id: 'home-4', title: 'Inicio', content: <p>Inicio</p> },
{ id: 'profile-4', title: 'Perfil', content: <p>Perfil</p> },
]}
/>
</VistaComponente>
{/* PROPS */}
<section className="mb-5" id="tabs-props">
<h2 className="h5 mb-3"> Props del componente</h2>
<table className="table table-bordered table-striped align-middle">
<section className='mb-5' id='tabs-props'>
<h2 className='h5 mb-3'> Props del componente</h2>
<table className='table table-bordered table-striped align-middle'>
<thead>
<tr>
<th scope="col">Prop</th>
<th scope="col">Tipo</th>
<th scope="col">Descripción</th>
<th>Prop</th>
<th>Tipo</th>
<th>Descripción</th>
</tr>
</thead>
<tbody>
@@ -150,14 +195,14 @@ export default function Page() {
<td>
<code>Tab[]</code>
</td>
<td>Lista de pestañas con ID, título y contenido.</td>
<td>Lista de pestañas con id, título y contenido.</td>
</tr>
<tr>
<td>
<code>style?</code>
</td>
<td>
<code>&quot;default&quot; | &quot;box&quot;</code>
<code>"default" | "box"</code>
</td>
<td>Estilo de las pestañas.</td>
</tr>
@@ -167,49 +212,82 @@ export default function Page() {
</td>
<td>
<code>
{"{ container?: string; tab?: string; content?: string; }"}
{'{ container?: string; tab?: string; content?: string; }'}
</code>
</td>
<td>
Clases CSS personalizables para diferentes partes del
componente.
</td>
<td>Clases CSS personalizables.</td>
</tr>
<tr>
<td>
<code>Tab.id</code>
<code>defaultActiveTab?</code>
</td>
<td>
<code>string</code>
</td>
<td>Identificador único de la pestaña.</td>
<td>
ID de la pestaña que se muestra inicialmente si no está
deshabilitada.
</td>
</tr>
</tbody>
</table>
<h3 className='h6 mt-4 mb-2'>
Props de cada pestaña (<code>Tab</code>)
</h3>
<table className='table table-sm table-bordered'>
<thead>
<tr>
<th>Prop</th>
<th>Tipo</th>
<th>Descripción</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>id</code>
</td>
<td>
<code>string</code>
</td>
<td>Identificador único.</td>
</tr>
<tr>
<td>
<code>Tab.title</code>
<code>title</code>
</td>
<td>
<code>string | JSX.Element</code>
</td>
<td>Título de la pestaña.</td>
<td>Título mostrado en la pestaña.</td>
</tr>
<tr>
<td>
<code>Tab.content</code>
<code>titleText?</code>
</td>
<td>
<code>string</code>
</td>
<td>Texto alternativo para tooltips si el título se trunca.</td>
</tr>
<tr>
<td>
<code>content</code>
</td>
<td>
<code>JSX.Element</code>
</td>
<td>Contenido dentro de la pestaña.</td>
<td>Contenido renderizado al activar la pestaña.</td>
</tr>
<tr>
<td>
<code>Tab.disabled?</code>
<code>disabled?</code>
</td>
<td>
<code>boolean</code>
</td>
<td>Si la pestaña debe estar deshabilitada.</td>
<td>Deshabilita la pestaña.</td>
</tr>
</tbody>
</table>
+9 -2
View File
@@ -1,6 +1,6 @@
'use client';
import ComponentCard from '@/components/component-card';
import ComponentCard from '@/components/docs/component-card';
import Image from 'next/image';
import React from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
@@ -46,7 +46,7 @@ export default function Page() {
customStyle={{ borderRadius: '10px' }}
>
{`npm i bootstrap bootstrap-icons
npm i --save-dev sass
npm i --save-dev sass@1.32.13
npm i --save-dev @types/bootstrap`}
</SyntaxHighlighter>
@@ -126,6 +126,13 @@ import BootstrapClient from "@/components/bootstrap-client";`}
<div className='col-lg-4'>
<ComponentCard title='Tabs' src='/tabs.png' href='tabs' />
</div>
<div className='col-lg-4'>
<ComponentCard
title='Progress Bar'
src='/progress-bar.png'
href='progress-bar'
/>
</div>
</div>
</div>
</main>
@@ -1,5 +1,5 @@
import { CodeBlock } from './code-block';
import Button from './button';
import Button from '../button';
import { useState } from 'react';
interface VistaComponenteProps {
+43
View File
@@ -0,0 +1,43 @@
import React from 'react';
interface EstatusProps {
estados: string[];
estadoActivo: number;
}
export default function ProgressBar({ estados, estadoActivo }: EstatusProps) {
return (
<>
<div className='track my-5 d-none d-md-flex'>
{estados.map((label, index) => (
<div
key={index}
className={`step ${index <= estadoActivo ? 'active' : ''}`}
>
<span className='icon'>{index + 1}</span>
<span className='text'>{label}</span>
</div>
))}
</div>
<div className='track my-5 d-flex d-md-none'>
<div className='step active'>
<span className='icon'>{estadoActivo + 1}</span>
<span className='text'>{estados[estadoActivo]}</span>
</div>
</div>
</>
);
}
function EstatusEskeleton() {
return (
<div className='box mb-3 '>
<span className='text-muted fw-bold'>Estatus:</span>
<div className='placeholder-glow'>
<span className='placeholder rounded col-12'></span>
</div>
</div>
);
}
export { EstatusEskeleton };
+30 -17
View File
@@ -1,48 +1,61 @@
import React, { useState, JSX } from "react";
import React, { useState, JSX } from 'react';
interface Tab {
id: string;
title: string | JSX.Element;
titleText?: string;
content: JSX.Element;
disabled?: boolean;
}
interface TabsProps {
tabs: Tab[];
style?: "default" | "box";
style?: 'default' | 'box';
className?: {
container?: string;
tab?: string;
content?: string;
};
defaultActiveTab?: string;
}
export default function Tabs({
tabs,
style = "default",
style = 'default',
className,
defaultActiveTab,
}: TabsProps) {
const [activeTab, setActiveTab] = useState(tabs[0]?.id || "");
const [activeTab, setActiveTab] = useState<string>(() => {
// 1) si se pasó defaultActiveTab y existe en tabs (y no está disabled), úsala
if (defaultActiveTab) {
const found = tabs.find((t) => t.id === defaultActiveTab && !t.disabled);
if (found) return found.id;
}
// 2) si no, buscar el primer tab habilitado
const firstEnabled = tabs.find((t) => !t.disabled);
return firstEnabled ? firstEnabled.id : tabs[0]?.id || '';
});
return (
<>
<ul className={`nav nav-tabs style-${style}`} id="myTab" role="tablist">
<ul className={`nav nav-tabs style-${style}`} id='myTab' role='tablist'>
{tabs.map((tab) => (
<li
key={tab.id}
className="nav-item"
role="presentation"
title={tab.title.toString()}
className='nav-item'
role='presentation'
title={
typeof tab.title === 'string' ? tab.title : tab.titleText || ''
}
>
<button
className={`nav-link ${tab.disabled ? "disabled" : ""} ${
activeTab === tab.id ? "active" : ""
className={`nav-link ${tab.disabled ? 'disabled' : ''} ${
activeTab === tab.id ? 'active' : ''
}`}
id={`tab-${tab.id}`}
data-bs-toggle="tab"
data-bs-toggle='tab'
data-bs-target={`#tab-pane-${tab.id}`}
type="button"
role="tab"
type='button'
role='tab'
aria-controls={`tab-pane-${tab.id}`}
aria-selected={activeTab === tab.id}
onClick={() => !tab.disabled && setActiveTab(tab.id)}
@@ -54,15 +67,15 @@ export default function Tabs({
</li>
))}
</ul>
<div className="tab-content mt-3" id="myTabContent">
<div className='tab-content mt-3' id='myTabContent'>
{tabs.map((tab) => (
<div
key={tab.id}
className={`tab-pane fade ${
activeTab === tab.id ? "show active" : ""
activeTab === tab.id ? 'show active' : ''
} ${className?.content}`}
id={`tab-pane-${tab.id}`}
role="tabpanel"
role='tabpanel'
aria-labelledby={`tab-${tab.id}`}
>
{activeTab === tab.id && tab.content}
+57
View File
@@ -0,0 +1,57 @@
.track {
position: relative;
background-color: #cecece;
height: 7px;
display: flex;
color: #fff;
border-radius: 10px;
.step {
flex-grow: 1;
margin-top: -18px;
text-align: center;
position: relative;
.icon {
display: inline-block;
width: 40px;
height: 40px;
line-height: 40px;
position: relative;
border-radius: 100%;
background: #cecece;
}
.text {
color: #000;
display: block;
margin-top: 7px;
}
&.active::before {
background: #007bff;
height: 7px;
position: absolute;
content: '';
width: 100%;
left: 0;
top: 18px;
}
&.active .icon {
background: #007bff;
color: #fff;
font-weight: bold;
}
}
}
.heatmap-legend {
width: 40px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
border: 1px solid #dee2e6;
}
+1 -1
View File
@@ -1,5 +1,5 @@
.tab-pane.fade {
transition: opacity 10s linear !important;
transition: opacity 0.4s linear !important;
}
.nav-tabs.style-box {
+1
View File
@@ -27,6 +27,7 @@ $theme-colors: map-merge($theme-colors, $custom-colors);
@import 'sidebar';
@import 'hover';
@import 'tabs';
@import 'progress-bar';
//Add additional custom code here