components
This commit is contained in:
+188
-60
@@ -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><Table /></code> permite mostrar datos
|
||||
tabulares con soporte para ordenamiento, paginación, renderizado
|
||||
personalizado, columnas clicables y estado de carga.
|
||||
El componente <code><Table /></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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// src/code-snippets/table-basic.ts
|
||||
export const tableBasicExample = `
|
||||
<Table headers={headers} data={baseData} />
|
||||
`;
|
||||
Reference in New Issue
Block a user