components

This commit is contained in:
jorgemike
2025-03-22 14:25:11 -06:00
parent e5f007044a
commit 3d5f888dcb
4 changed files with 246 additions and 60 deletions
+188 -60
View File
@@ -1,22 +1,36 @@
"use client";
import React, { useState } from "react";
import Table, { Header, TablaSkeleton } from "@/components/table";
import React from "react";
import VistaComponente from "@/components/vista-componente";
import { tableBasicExample } from "@/snippets/table/table-basic";
export default function Page() {
const headers: Header<{ name: string; age: number; email: string; acciones?: null }>[] = [
const headers: Header<{
name: string;
age: number;
email: string;
acciones?: null;
}>[] = [
{ key: "name", label: "Nombre", width: "30%" },
{ key: "age", label: "Edad", width: "15%" },
{ key: "email", label: "Correo", width: "40%" },
];
const data = [
const baseData = [
{ name: "Juan", age: 20, email: "juan@example.com" },
{ name: "María", age: 25, email: "maria@example.com" },
{ name: "Pedro", age: 30, email: "pedro@example.com" },
{ name: "Luisa", age: 28, email: "luisa@example.com" },
];
const [currentPage, setCurrentPage] = useState(1);
const [search, setSearch] = useState("");
const filteredData = baseData.filter((item) =>
item.name.toLowerCase().includes(search.toLowerCase())
);
return (
<div className="container my-5">
<h1 className="mb-4 display-5">
@@ -24,74 +38,188 @@ export default function Page() {
</h1>
<p className="mb-4 text-muted">
El componente <code>&lt;Table /&gt;</code> permite mostrar datos
tabulares con soporte para ordenamiento, paginación, renderizado
personalizado, columnas clicables y estado de carga.
El componente <code>&lt;Table /&gt;</code> permite mostrar datos en
forma tabular, con funcionalidades como ordenamiento, paginación,
renderizado personalizado, clic en filas y un estado de carga esqueleto.
</p>
<hr className="my-5" />
<h2 className="h5">🔹 Uso básico</h2>
<Table headers={headers} data={data} />
{/* USO BÁSICO */}
<VistaComponente
title="Uso básico"
description="Tabla simple con headers y datos sin configuración adicional."
code={tableBasicExample}
>
<Table headers={headers} data={baseData} />
</VistaComponente>
<hr className="my-5" />
{/* FILAS CLICABLES */}
<VistaComponente
title="🔱 Filas clicables"
description="Usa `onRowClick` para ejecutar acciones al hacer clic en una fila."
>
<Table
headers={headers}
data={baseData}
onRowClick={(row) => alert(`Seleccionaste a ${row.name}`)}
/>
</VistaComponente>
<h2 className="h5">🖱 Filas clicables</h2>
<p className="mb-2">
Usa la propiedad <code>onRowClick</code> para manejar interacciones por
fila.
</p>
<Table
headers={headers}
data={data}
onRowClick={(row) => alert(`Seleccionaste a ${row.name}`)}
/>
{/* PAGINACIÓN */}
<VistaComponente
title="📄 Paginación personalizada"
description="Controla cuántas filas se muestran por página con `rowsPerPage`."
>
<Table headers={headers} data={baseData} rowsPerPage={2} />
</VistaComponente>
<hr className="my-5" />
{/* CONTROL DE PÁGINA EXTERNO */}
<VistaComponente
title="🔀 Paginación controlada externamente"
description="Usa `controlledPage` y `onPageChange` para controlar la página actual desde el estado del componente padre."
>
<Table
headers={headers}
data={baseData}
rowsPerPage={2}
controlledPage={currentPage}
onPageChange={setCurrentPage}
/>
</VistaComponente>
<h2 className="h5">📄 Paginación personalizada</h2>
<p className="mb-2">
Usa <code>rowsPerPage</code> para definir cuántas filas mostrar por
página.
</p>
<Table headers={headers} data={data} rowsPerPage={2} />
{/* RENDER PERSONALIZADO */}
<VistaComponente
title="🎨 Render personalizado por celda"
description="Personaliza cómo se muestran ciertas columnas usando la propiedad `render`."
>
<Table
headers={[
...headers,
{
key: "acciones",
label: "Acciones",
width: "15%",
render: (_, row) => (
<button
className="btn btn-sm btn-primary"
onClick={() => alert(row.email)}
>
Ver correo
</button>
),
},
]}
data={baseData.map((d) => ({ ...d, acciones: null }))}
/>
</VistaComponente>
<hr className="my-5" />
{/* PERSONALIZACIÓN DE ANCHO */}
<VistaComponente
title="🖖 Personalización del tamaño de columnas"
description="Puedes controlar el ancho de cada columna usando la propiedad `width` dentro de cada header."
>
<Table
headers={[
{ key: "name", label: "Nombre", width: "50%" },
{ key: "age", label: "Edad", width: "15%" },
{ key: "email", label: "Correo electrónico", width: "35%" },
]}
data={baseData}
/>
</VistaComponente>
<h2 className="h5">🎨 Render personalizado por celda</h2>
<p>
Puedes usar la propiedad <code>render</code> dentro de cada{" "}
<code>header</code> para personalizar cómo se muestra una columna.
</p>
<Table
headers={[
...headers,
{
key: "acciones",
label: "Acciones",
width: "15%",
render: (_, row) => (
<button
className="btn btn-sm btn-primary"
onClick={() => alert(row.email)}
>
Ver correo
</button>
),
},
]}
data={data.map((d) => ({ ...d, acciones: null as null }))}
/>
{/* ANCHO AUTOMÁTICO */}
<VistaComponente
title="📏 Columnas con tamaño automático"
description="Si no defines el ancho de las columnas, el navegador ajusta el tamaño según el contenido."
>
<Table
headers={[
{ key: "name", label: "Nombre" },
{ key: "age", label: "Edad" },
{ key: "email", label: "Correo electrónico" },
]}
data={[
{ name: "Ana", age: 19, email: "ana@example.com" },
{
name: "Luis Alberto Martínez",
age: 34,
email: "luis.martinez@correo-ejemplo.com",
},
{ name: "Carla", age: 27, email: "carla@example.com" },
]}
/>
</VistaComponente>
<hr className="my-5" />
{/* SKELETON */}
<VistaComponente
title="⏳ Tabla en estado de carga"
description="El componente `TablaSkeleton` simula una tabla vacía mientras se cargan los datos."
>
<TablaSkeleton rows={3} columns={4} />
</VistaComponente>
<h2 className="h5"> Tabla en estado de carga</h2>
<p className="mb-2">
El componente <code>TablaSkeleton</code> muestra una tabla de ejemplo
mientras se cargan los datos. Puedes personalizar <code>rows</code> y{" "}
<code>columns</code>.
</p>
<TablaSkeleton rows={3} columns={4} />
{/* ROW KEY */}
<VistaComponente
title="🆔 Usar identificadores únicos con `rowKey`"
description="Proporciona una función para identificar cada fila de forma única."
>
<Table
headers={headers}
data={baseData.map((item, index) => ({ ...item, id: index }))}
rowKey={(row) => row.email}
/>
</VistaComponente>
{/* FILTRO */}
<VistaComponente
title="🔍 Filtro simple con input externo"
description="Filtra los datos con un input controlado."
>
<input
className="form-control mb-3"
placeholder="Buscar por nombre..."
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<Table headers={headers} data={filteredData} />
</VistaComponente>
{/* PROPS */}
<section className="mb-5">
<h2 className="h5"> Props del componente</h2>
<ul>
<li>
<code>headers</code>: Columnas de la tabla (con key, label, render,
width...)
</li>
<li>
<code>data</code>: Datos a mostrar.
</li>
<li>
<code>rowsPerPage?</code>: Número de filas por página (por defecto:
10).
</li>
<li>
<code>onRowClick?</code>: Función al hacer clic en una fila.
</li>
<li>
<code>rowKey?</code>: Función que retorna un ID único para cada
fila.
</li>
<li>
<code>className?</code>: Estilos personalizados (tabla, fila, celda,
header).
</li>
<li>
<code>controlledPage?</code>: Página actual controlada externamente.
</li>
<li>
<code>onPageChange?</code>: Callback para cambiar la página.
</li>
</ul>
</section>
</div>
);
}
+13
View File
@@ -0,0 +1,13 @@
export const CodeBlock = ({ code }: { code: string }) => (
<div className="position-relative">
<pre className="bg-dark text-white p-3 rounded overflow-auto mb-0">
<code>{code}</code>
</pre>
<button
className="btn btn-sm btn-light position-absolute top-0 end-0 m-2"
onClick={() => navigator.clipboard.writeText(code)}
>
Copiar
</button>
</div>
);
+41
View File
@@ -0,0 +1,41 @@
import React, { useState } from "react";
import { CodeBlock } from "./code-block";
interface VistaComponenteProps {
title: string;
description?: string;
code?: string;
children: React.ReactNode;
}
export default function VistaComponente({
title,
description,
code,
children,
}: VistaComponenteProps) {
const [showCode, setShowCode] = useState(false);
return (
<section className="mb-5">
<h2 className="h5 mb-2">{title}</h2>
{description && <p className="text-muted mb-2">{description}</p>}
<div className="p-3 border rounded bg-light mb-3">
{children}
{code && (
<div className="d-flex flex-column gap-2">
<button
className="btn btn-sm btn-outline-secondary align-self-start"
onClick={() => setShowCode((prev) => !prev)}
>
{showCode ? "Ocultar código" : "Ver código y copiar"}
</button>
{showCode && <CodeBlock code={code} />}
</div>
)}
</div>
</section>
);
}
+4
View File
@@ -0,0 +1,4 @@
// src/code-snippets/table-basic.ts
export const tableBasicExample = `
<Table headers={headers} data={baseData} />
`;