Skip to content
GitHub

Biblioteca de Componentes UI

La Biblioteca de Componentes UI proporciona un conjunto completo de componentes SolidJS reutilizables organizados siguiendo los principios de Diseño Atómico. Esta biblioteca sirve como la base para todos los elementos de interfaz de usuario en la aplicación frontend Sevastopol, asegurando consistencia, mantenibilidad y seguridad de tipos en todas las vistas de negocio.

La jerarquía de componentes consiste en cuatro capas:

  • Átomos: Campos de formulario primitivos, botones y elementos UI básicos (Input, Select, Button, SearchBox, etc.)
  • Moléculas: Patrones UI compuestos (PageHeader, StatsGrid, FilterBar, StatCard)
  • Organismos: Componentes interactivos complejos (DataTable, Sidebar, Modal)
  • Plantillas: Layouts a nivel de página (IslandBase)

Todos los componentes soportan modo oscuro mediante propiedades CSS personalizadas y siguen patrones de estilos consistentes definidos en sevastopol/src/lib/ui.ts.


Visión General de la Arquitectura de Componentes

Section titled “Visión General de la Arquitectura de Componentes”
flowchart LR
subgraph TPL["Plantillas"]
  ISB["IslandBase.tsx<br/>Plantilla de Layout Estándar"]
end
subgraph MOL["Moléculas"]
  BC["Breadcrumb.tsx<br/>prop: text"]
  FB["FilterBar.tsx<br/>Búsqueda + Filtros Personalizados"]
  SG["StatsGrid.tsx<br/>Contenedor de Cuadrícula de Métricas"]
  PH["PageHeader.tsx<br/>title · description · actions"]
  SC["StatCard.tsx<br/>label · value · sub"]
end
subgraph ORG["Organismos"]
  DT["DataTable.tsx<br/>props: items, headers, sumKeys"]
  MD["Modal.tsx<br/>props: open, onClose, children"]
end
subgraph ATM["Átomos"]
  SB["SearchBox.tsx<br/>value · onChange · delay"]
  TP["TenantPicker.tsx<br/>tenants · value · onChange"]
  FI["Fields.tsx<br/>Input · Select · Button · Checkbox · TextArea"]
  TA["TableAction.tsx<br/>variant · icon · onClick"]
  PG["Pagination.tsx<br/>total · limit · offset"]
end
ISB --> BC
ISB --> FB
ISB --> SG
ISB --> PH
FB -->|slots| SB
FB -->|slots| TP
SG -->|slots| SC
SG -->|slots| FI
PH -->|slots| FI
DT --> SB
DT --> TA
DT --> PG
MD -->|slots| FI

Diagrama: Jerarquía de Componentes con Ubicaciones de Archivos

Esta arquitectura habilita máxima reutilización. Por ejemplo, DataTable se usa en PayrollViewIsland, EmployeesViewIsland, ContractsViewIsland, TenantsViewIsland y PlanContableViewIsland, cada uno configurado con diferentes definiciones de encabezado desde sevastopol/src/config/headers.ts


Los átomos son los bloques de construcción fundamentales de la UI - componentes primitivos que no pueden descomponerse más. Todos los átomos siguen patrones de diseño consistentes con soporte para modo oscuro y accesibilidad de teclado.

El componente Input envuelve campos de entrada HTML nativos con estilos estandarizados y gestión de etiquetas.

API del Componente:

PropTipoRequeridoDescripción
labelstringTexto de etiqueta mostrado sobre el input
namestringNombre del campo de formulario
…restJSX.InputHTMLAttributesNoAtributos nativos de input (type, placeholder, value, etc.)

Ejemplo de Uso:

<Input
label="RUT"
name="rut"
required
placeholder="12345678-9"
value={editing()?.rut ?? ""}
/>

El componente Select proporciona un menú desplegable con manejo de opciones con seguridad de tipos. Normaliza valores a strings para prevenir problemas de coerción de tipos.

API del Componente:

PropTipoRequeridoDescripción
labelstringNoTexto de etiqueta mostrado sobre el select
namestringNombre del campo de formulario
optionsOption[]Array de objetos {id, nombre}
valuestring | number | nullNoValor seleccionado actual (normalizado a string)
placeholderstringNoTexto placeholder (predeterminado: ”—“)
density"normal" | "compact"NoVariante de densidad visual

Definición de Tipo:

type Option = {
id: string;
nombre: string;
[key: string]: any;
};

Característica Clave - Normalización de Valores: El componente normaliza explícitamente tanto el valor actual como los IDs de las opciones a strings para prevenir desajustes de tipo React value={0} vs value="0"

El componente Button proporciona tres variantes visuales con soporte para iconos.

API del Componente:

PropTipoRequeridoDescripción
labelstringTexto del botón
iconstring | JSX.ElementNoIcono (emoji o componente SVG)
variant"primary" | "secondary" | "danger"NoEstilo visual (predeterminado: “primary”)
...restJSX.ButtonHTMLAttributesNoAtributos nativos de botón

Estilos de Variantes:

VarianteEstilo
primaryFondo negro (oscuro: blanco), alto contraste
secondaryFondo blanco con borde, sutil
dangerFondo Rose-600, para acciones destructivas

Checkbox estilizado con etiqueta opcional y mensaje personalizado.

API del Componente:

PropTipoRequeridoDescripción
labelstringNoTexto de etiqueta sobre el checkbox
namestringNombre del campo de formulario
messagestringNoTexto mostrado junto al checkbox (predeterminado: “Activar”)
...restJSX.InputHTMLAttributesNoAtributos nativos de checkbox

Entrada de texto multi-línea con estilos consistentes.

API del Componente:

PropTipoRequeridoDescripción
labelstringTexto de etiqueta mostrado sobre el textarea
namestringNombre del campo de formulario
...restJSX.TextareaHTMLAttributesNoAtributos nativos de textarea

Componente de entrada de búsqueda con debounce que retrasa las llamadas onChange para reducir peticiones API durante la escritura.

API del Componente:

PropTipoRequeridoDescripción
placeholderstringNoTexto placeholder
valuestringValor de búsqueda actual
delaynumberNoRetraso de debounce en ms (predeterminado: 300)
onChange(v: string) => voidManejador de cambio (llamado después del retraso)
classstringNoClases CSS adicionales

El componente usa createEffect con setTimeout para implementar el debouncing.

Ejemplo de Uso:

<SearchBox
placeholder="Buscar por nombre, rut o email..."
value={q()}
onChange={setQ}
/>

Controles de paginación simples para navegar datos paginados.

API del Componente:

PropTipoRequeridoDescripción
totalnumberNúmero total de registros
limitnumberRegistros por página
offsetnumberOffset actual
onPrev() => voidManejador de página anterior
onNext() => voidManejador de página siguiente

El componente calcula automáticamente estados deshabilitados basándose en offset y total

Componente de overlay de diálogo para formularios y confirmaciones.

API del Componente:

PropTipoRequeridoDescripción
openbooleanControla la visibilidad del modal
titlestringNoTítulo del modal
onClose() => voidManejador de cierre
childrenJSX.ElementContenido del modal
widthClassstringNoAncho personalizado (predeterminado: “w-[min(100vw-2rem,1400px)]“)
titleClassstringNoEstilos personalizados del título

El modal usa un backdrop con comportamiento de click-para-cerrar

<div class="absolute inset-0 bg-black/60 dark:bg-black/80 backdrop-blur-sm"
onClick={props.onClose}/>

Menú desplegable para seleccionar organizaciones tenant, típicamente usado dentro del StatsGrid de IslandBase.

API del Componente:

PropTipoRequeridoDescripción
tenantsTenant[]Tenants disponibles
valuestringID del tenant seleccionado
onChange() => voidManejador de selección

Tipo Tenant:

type Tenant = {
id: string;
rut: string;
business_name: string;
// ... campos adicionales
};

El componente usa la clase utilitaria estandarizada selectCls de sevastopol/src/lib/ui.ts para estilos consistentes en todos los elementos select.

Botón de acción basado en iconos optimizado para filas de tablas de datos. Proporciona estados hover sutiles con coloración basada en variante.

API del Componente:

Prop Tipo Requerido Descripción

PropTipoRequeridoDescripción
onClick() => voidManejador de click
iconanyNoComponente de icono o emoji
labelstringNoTexto de tooltip/aria-label
variantstringNoVariante de estilo visual
classstringNoClases CSS adicionales
disabledbooleanNoEstado deshabilitado

Iconos Integrados:

El componente exporta un conjunto de iconos SVG pre-construidos a través del objeto Icons:

  • Icons.Edit - Icono de lápiz
  • Icons.Delete - Icono de papelera
  • Icons.Eye - Icono de vista/previsualización
  • Icons.Database - Icono de base de datos
  • Icons.Download - Icono de flecha de descarga

Ejemplo de Uso:

<TableAction
label="Editar"
variant="edit"
icon={ActionIcons.Edit}
onClick={() => startEdit(row)}
/>

Las moléculas son componentes compuestos construidos a partir de átomos. Estos se importan desde @/components/molecules y se usan principalmente dentro de la plantilla IslandBase.

Muestra título de página, descripción y botones de acción.

Props: title (string), description (string), actions (slot JSX.Element)

Contenedor para mostrar tarjetas de métricas en una cuadrícula responsive.

Uso: Acepta prop children conteniendo componentes StatCard y opcionalmente TenantPicker

Tarjeta de visualización de métrica única con etiqueta, valor y subtítulo.

Props: label (string), value (string | number), sub (string), highlight (boolean), color (string)

Contenedor para cuadro de búsqueda y controles de filtro personalizados.

Uso: Acepta prop children, típicamente conteniendo SearchBox y componentes Select de filtro.

Muestra ruta de navegación jerárquica.

Props: text (string) - se muestra en formato “MÓDULO · TÍTULO”


El DataTable es el componente más complejo y ampliamente utilizado en el sistema, proporcionando una tabla rica en características con filtrado, ordenamiento, paginación y renderizado personalizable.

flowchart LR
subgraph UTIL["Funciones Utilitarias"]
  CS["changeSort(key)<br/>alterna sortDesc"]
  FN["formatNumber(value)<br/>locale de-DE"]
  FD["formatDate(iso)<br/>slice(0,10)"]
  INU["isNumeric(value)<br/>verificación de tipo"]
end
DT["DataTable<br/>(items, headers, sumKeys)"]
subgraph SIG["Señales SolidJS (Estado)"]
  SK["sortKey<br/>createSignal()"]
  SD["sortDesc<br/>createSignal()"]
  PGN["page<br/>createSignal()"]
  FLT["filter<br/>createSignal()"]
end
subgraph DER["Estado Derivado<br/>(createMemo)"]
  FILT["filteredItems()<br/>filtra por año/mes"]
  SORT["sortedItems()<br/>ordena por columna"]
  PAG["paginated()<br/>PAGE_SIZE = 10"]
end
CELL1["renderizado de celda"]
CELL2["renderizado de celda"]
TABLE["con encabezados + filas"]
DT --> SK
DT --> SD
DT --> PGN
DT --> FLT
CS --> SK
CS --> SD
FLT --> FILT
SK --> SORT
SD --> SORT
FILT --> SORT
SORT --> PAG
PGN --> PAG
PAG --> TABLE
FN --> CELL1
FD --> CELL2
INU --> CELL1
INU --> CELL2
TABLE --> CELL1
TABLE --> CELL2

Diagrama: Arquitectura Interna de DataTable - Señales y Memos

PropTipoRequeridoDescripción
itemsany[]Array de objetos de datos a mostrar
headersHeaderConfig[]Array de configuración de columnas
sumKeysstring[]NoClaves a sumar en fila de pie
chartsbooleanNoHabilitar visualización de gráficos (predeterminado: true)
variant"default" | "island"NoVariante visual
manualPaginationbooleanNoDeshabilitar paginación interna
actions{ onEdit?, onDelete? }NoManejadores de acción de fila

El tipo HeaderConfig define el comportamiento y renderizado de columnas:

type HeaderConfig = {
key: string; // Clave de propiedad de datos
label: string; // Texto de encabezado de columna
sortable?: boolean; // Habilitar ordenamiento (predeterminado: true)
align?: "left" | "center" | "right";
render?: (value: any, row: any) => any; // Renderizador personalizado
};

El componente auto-detecta columnas año, mes, year, month y genera menús desplegables de filtro

const filterFields = headers.filter((h) =>
["año", "mes", "year", "month"].includes(h.key)
);

Hacer clic en encabezados de columna alterna la dirección de ordenamiento. El estado de ordenamiento se preserva vía señales.

const [sortKey, setSortKey] = createSignal(headers[0]?.key ?? "");
const [sortDesc, setSortDesc] = createSignal(true);

Los valores numéricos se formatean automáticamente con separadores de miles usando locale alemán (de-DE).

function formatNumber(value: any): string {
const num = typeof value === "string" ? parseFloat(value) : Number(value);
if (isNaN(num)) return "0";
return num.toLocaleString("de-DE", {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
});
}

Las fechas se detectan automáticamente por nombre de columna (fecha) y se formatean a YYYY-MM-DD

Cuando se proporciona sumKeys, la tabla muestra una fila de pie con totales o promedios de columnas

{sumKeys.length > 0 && (
<tfoot class="sticky bottom-0 bg-emerald-50 dark:bg-emerald-900/20">
<tr class="font-bold">
{/* Calcula sumas/promedios para claves especificadas */}
</tr>
</tfoot>
)}

Acciones opcionales de editar/eliminar aparecen en una columna dedicada

Paginación integrada con tamaño de página configurable (predeterminado: 10 filas)

VarianteDescripciónCaso de Uso
defaultTabla estándar con contenedor externoTablas independientes
islandContenedor flex-1 con min-height para layouts de página completaTablas dentro de IslandBase

El componente IslandBase es una plantilla estandarizada que proporciona estructura de layout consistente para todas las vistas de negocio. Opera en dos modos: Modo Simple (contenedor básico) y Modo Estándar (layout completo con selección de tenant, estadísticas, filtros).

flowchart TB
ISB["Plantilla IslandBase"]
subgraph TOP["Barra Superior"]
  CLOSE["Botón Cerrar<br/>(handleClose)"]
  TITLE["Título Simple<br/>(opcional)"]
end
subgraph LAY["Elementos de Layout<br/>(prop module existe)"]
  BC["Breadcrumb<br/>(MÓDULO · TÍTULO)"]
  PH["PageHeader<br/>(title · description · actions)"]
  SG["StatsGrid<br/>(métricas + TenantPicker)"]
  FB["FilterBar<br/>(SearchBox + filtros)"]
end
CONTENT["Área de Contenido<br/>children (contenido específico de vista)"]
ISB --> TOP
ISB --> LAY
ISB --> CONTENT
BC --> PH
PH --> SG
SG --> FB
FB --> CONTENT
ISB -.->|"sin prop module"| CONTENT

Diagrama: Flujo de Layout de Plantilla IslandBase

El componente acepta un conjunto completo de props organizadas por funcionalidad:

PropTipoRequeridoDescripción
titlestringNoTítulo de página (usado en ambos modos)
subtitlestringNoSubtítulo (solo Modo Simple)
childrenJSX.ElementContenido específico de vista
onClose() => voidNoManejador de cierre personalizado (predeterminado: dispatch sidebar:navigate)
isLoadingbooleanNoMostrar overlay de carga
hideCloseButtonbooleanNoOcultar el botón de cierre

Props de Layout Estándar (Activadas por module)

Section titled “Props de Layout Estándar (Activadas por module)”
PropTipoRequeridoDescripción
modulestringNoNombre de módulo (ej: “REMUNERACIONES”) - activa Modo Estándar
descriptionstringNoTexto de descripción bajo el título
onNew() => voidNoManejador para botón “Nuevo”
newLabelstringNoEtiqueta personalizada para botón nuevo (predeterminado: ”+ Nuevo”)
actionsJSX.ElementNoSlot de botones de acción personalizados
PropTipoRequeridoDescripción
tenantsTenant[]NoLista de tenants disponibles
selectedTenantIdstringNoID del tenant actualmente seleccionado
onTenantChange(id: string) => voidNoManejador de selección de tenant
PropTipoRequeridoDescripción
statsJSX.ElementNoSlot de tarjetas de estadísticas personalizadas
searchPlaceholderstringNoPlaceholder del cuadro de búsqueda
searchValuestringNoValor de búsqueda actual
onSearchChange(val: string) => voidNoManejador de cambio de búsqueda
filtersJSX.ElementNoSlot de filtros personalizados

El componente cambia automáticamente a Modo Estándar cuando se proporciona la prop module.

<Show when={props.module} fallback={props.children}>
{/* Renderizado de Layout Estándar */}
</Show>

En Modo Estándar, el componente renderiza una estructura consistente

  1. Breadcrumb - Muestra MÓDULO · TÍTULO en mayúsculas
  2. PageHeader - Título, descripción y botones de acción
  3. StatsGrid - Tarjetas de métricas con TenantPicker integrado
  4. FilterBar - Cuadro de búsqueda y filtros personalizados
  5. Área de Contenido - Children específicos de vista con manejo de scroll

Cuando se proporciona la prop tenants, el componente renderiza automáticamente un TenantPicker dentro del StatsGrid.

<Show when={props.tenants && props.onTenantChange}>
<div class="bg-white dark:bg-zinc-800 rounded-xl shadow p-4">
<div class="text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">
Organización
</div>
<TenantPicker
tenants={props.tenants!}
value={props.selectedTenantId || ""}
onChange={props.onTenantChange!}
/>
</div>
</Show>

El componente proporciona un overlay de carga integrado con spinner

<Show when={props.isLoading}>
<div class="absolute inset-0 z-50 flex items-center justify-center
bg-white/50 dark:bg-zinc-900/50 backdrop-blur-sm">
<div class="w-8 h-8 border-4 border-indigo-500 border-t-transparent
rounded-full animate-spin" />
</div>
</Show>

Patrón de Configuración: Sistema de Encabezados

Section titled “Patrón de Configuración: Sistema de Encabezados”

El sistema usa un enfoque de configuración de encabezados centralizado donde las definiciones de columnas se almacenan en config/headers.ts y se reutilizan a través de vistas. Este patrón asegura consistencia y habilita lógica de renderizado compleja.

Estructura de Configuración de Encabezados

Section titled “Estructura de Configuración de Encabezados”
flowchart LR
CFG["headers.ts<br/>(Configuración)"]
subgraph FOOT["Claves de Suma de Pie"]
  FSP["FOOTER_SUM_KEYS_PROVEEDORES"]
  FSC["FOOTER_SUM_KEYS_CLIENTES"]
end
subgraph HEAD["Definiciones de Encabezados"]
  HPROV["HEADERS_PROVEEDORES"]
  HCLI["HEADERS_CLIENTES"]
  HPAY["HEADERS_PAYROLL"]
  HEMP["HEADERS_EMPLEADOS"]
  HCON["HEADERS_CONTRATOS"]
  HVAC["HEADERS_VACATIONS"]
end
subgraph FMT["Utilidades de Formato"]
  CLP["fmtCLP()"]
  DATE["fmtDate()"]
  INT["fmtInt()"]
end
CFG --> FSP
CFG --> FSC
CFG --> HPROV
CFG --> HCLI
CFG --> HPAY
CFG --> HEMP
CFG --> HCON
CFG --> HVAC
HPAY --> CLP
HVAC --> DATE
HPAY --> INT

Diagrama: Arquitectura del Sistema de Configuración de Encabezados

Ejemplos de Configuraciones de Encabezados

Section titled “Ejemplos de Configuraciones de Encabezados”
export const HEADERS_PROVEEDORES = [
{ key: "rut_proveedor", label: "RUT" },
{ key: "razon_social", label: "Razón Social" },
{ key: "año", label: "Año" },
{ key: "mes", label: "Mes" },
{ key: "total_documentos", label: "# Docs" },
// ... más columnas
];

Encabezados con Renderizadores Personalizados

Section titled “Encabezados con Renderizadores Personalizados”

Los renderizadores personalizados habilitan formato complejo y renderizado JSX:

export const HEADERS_PAYROLL = [
{ key: "periodo", label: "Periodo" },
{ key: "empleado_nombre", label: "Empleado" },
{
key: "total_haberes",
label: "Haberes",
render: (v: any) => fmtCLP(num(v)),
},
{
key: "total_descuentos",
label: "Descuentos",
render: (v: any) => fmtCLP(num(v)),
},
// ...
];

Encabezados con Renderizadores Conscientes de Fila

Section titled “Encabezados con Renderizadores Conscientes de Fila”

Los renderizadores pueden acceder tanto al valor de celda como al objeto de fila completo:

export const HEADERS_CONTRATOS = [
{ key: "id", label: "Trabajador" }, // Renderizado complejo en componente
{ key: "numero_contrato", label: "N°", render: (v: any) => v ?? "—" },
{ key: "tipo_contrato", label: "Tipo" },
{ key: "fecha_inicio", label: "Inicio", render: (v: any) => fmtDate(v) },
{
key: "semaforo",
label: "Estado",
render: (v: any, row: Contrato) => v ?? row.estado ?? "—",
},
// ...
];

Correspondiendo a cada conjunto de encabezados, las claves de suma definen qué columnas agregar en el pie:

export const FOOTER_SUM_KEYS_PROVEEDORES = [
"total_documentos",
"total_neto",
"total_iva_recuperable",
"total_iva_no_recuperable",
"total_compras",
"total_exento",
"total_activo_fijo",
"total_iva_activo_fijo",
"total_facturas",
"total_notas_credito",
"total_notas_debito",
"promedio_monto_documentos",
];

Los componentes island importan configuraciones de encabezados y las pasan a DataTable:

import { HEADERS_PAYROLL } from "@/config/headers";
<DataTable
items={filteredPayrolls()}
headers={HEADERS_PAYROLL}
sumKeys={["total_haberes", "total_descuentos", "total_liquido"]}
actions={{
onEdit: (row) => handleEdit(row),
onDelete: (row) => handleDelete(row),
}}
/>

La biblioteca de componentes UI usa un sistema de tipos completo definido en types/ui.ts para asegurar seguridad de tipos a través de todos los componentes.

Tipo Option

Tipo de opción genérico usado por componentes Select:

export type Option = {
id: string;
nombre: string;
[key: string]: any;
};

Tipo HeaderConfig

Define configuración de columna de tabla:

export type HeaderConfig = {
key: string; // Clave de propiedad en objeto de datos
label: string; // Etiqueta de visualización
sortable?: boolean; // Habilitar ordenamiento (predeterminado: true)
align?: "left" | "center" | "right";
render?: (value: any, row: any) => any; // Función de renderizado personalizado
};

Tipos de Estado y Badge

export type StatusType =
| "ACTIVO"
| "INACTIVO"
| "PENDIENTE"
| "APROBADO"
| "RECHAZADO";
export type Semaforo = "VENCIDO" | "POR_VENCER" | "VIGENTE" | null;
export type ToastKind = "ok" | "error" | "info" | "warning";

Metadatos de Paginación

export type PaginationMeta = {
page: number;
pageSize: number;
totalPages: number;
totalRecords: number;
};

Props de Stat Card

export type StatCardProps = {
label: string;
value: string | number;
sub: string;
highlight?: boolean;
color?: string;
};

Patrón 1: Modal de Formulario con Componentes de Campo

Section titled “Patrón 1: Modal de Formulario con Componentes de Campo”

Patrón estándar para modales de crear/editar usado a través de la aplicación:

<Modal
open={showFormModal()}
onClose={() => setShowFormModal(false)}
title={editing() ? "Editar" : "Nuevo"}
>
<form onSubmit={handleSave}>
<div class="grid grid-cols-2 gap-x-4 gap-y-3">
<Input label="RUT" name="rut" required value={editing()?.rut ?? ""} />
<Input label="Nombre" name="nombre" required value={editing()?.nombre ?? ""} />
<Select
label="Tipo"
name="tipo"
options={tipoOptions}
value={editing()?.tipo ?? ""}
/>
<Checkbox label="Activo" name="activo" checked={editing()?.activo ?? true} />
</div>
<div class="flex gap-3 mt-6 justify-end">
<Button label="Cancelar" variant="secondary" onClick={handleCancel} />
<Button label="Guardar" variant="primary" type="submit" />
</div>
</form>
</Modal>

Patrón estándar para vistas de lista con filtrado y acciones:

<IslandBase
module="REMUNERACIONES"
title="Liquidaciones"
description="Gestión de liquidaciones de sueldo"
onNew={handleNew}
searchValue={q()}
onSearchChange={setQ}
stats={<><StatCard label="Total" value={total} sub="Liquidaciones" /></>}
tenants={tenants()}
selectedTenantId={selectedTenantId()}
onTenantChange={handleTenantChange}
>
<DataTable
items={filteredItems()}
headers={HEADERS_PAYROLL}
sumKeys={["total_haberes", "total_descuentos", "total_liquido"]}
actions={{
onEdit: handleEdit,
onDelete: handleDelete
}}
/>
</IslandBase>

Patrón 3: Renderizadores Personalizados de Tabla

Section titled “Patrón 3: Renderizadores Personalizados de Tabla”

Usando componentes TableAction para acciones de fila:

const tableHeaders: HeaderConfig[] = [
{ key: "rut", label: "RUT" },
{ key: "nombre", label: "Nombre" },
{
key: "actions",
label: "",
render: (_: any, row: any) => (
<div class="flex items-center justify-end gap-1">
<TableAction
label="Editar"
variant="edit"
icon={ActionIcons.Edit}
onClick={() => handleEdit(row)}
/>
<TableAction
label="Borrar"
variant="delete"
icon={ActionIcons.Delete}
onClick={() => handleDelete(row)}
/>
</div>
)
}
];

Todos los componentes atómicos se exportan centralmente desde components/atoms/index.ts para importación conveniente:

src/components/atoms/index.ts
export { TenantPicker, type Tenant } from "./TenantPicker";
export { ToastProvider, useToast } from "./ToastProvider";
export { Input, Select, TextArea, Checkbox, Button } from "./Fields";
export { Modal } from "./Modal";
export { SearchBox } from "./SearchBox";
export { Pagination } from "./Pagination";
export { TableAction, Icons as ActionIcons } from "./TableAction";
export { DataTable } from "./DataTable";

Esto habilita importaciones limpias en componentes island:

import {
Input,
Select,
Button,
Modal,
DataTable,
TableAction,
ActionIcons,
} from "@/components/atoms";

La biblioteca de componentes usa constantes de estilos centralizadas desde

export const inputCls =
"w-full bg-white dark:bg-zinc-800 text-black dark:text-white ...";
export const selectCls =
"w-full bg-white dark:bg-zinc-800 ... bg-[url('data:image/svg+xml;...')]";
export const chipCls = "px-2 py-0.5 rounded text-[11px]";

Estas clases aseguran estilos consistentes a través de todos los componentes de formulario con soporte integrado de modo oscuro. El selectCls incluye una flecha desplegable SVG personalizada vía URL de datos

Configuración de Tailwind: El modo oscuro está habilitado vía estrategia de clase en

darkMode: ["class", '[data-theme="dark"]'];

ComponenteTipoUbicaciónProps Clave
InputÁtomoFields.tsxlabel, name, value
SelectÁtomoFields.tsxoptions, value, onChange
ButtonÁtomoFields.tsxlabel, variant, icon, onClick
CheckboxÁtomoFields.tsxname, checked, message
TextAreaÁtomoFields.tsxlabel, name
SearchBoxÁtomoSearchBox.tsxvalue, onChange, delay
PaginationÁtomoPagination.tsxtotal, limit, offset, onPrev, onNext
TableActionÁtomoTableAction.tsxvariant, icon, onClick
TenantPickerÁtomoTenantPicker.tsxtenants, value, onChange
ModalOrganismoModal.tsxopen, onClose, children
DataTableOrganismoDataTable.tsxitems, headers, sumKeys, actions
PageHeaderMoléculamolecules/PageHeader title, description, actions
StatsGridMoléculamolecules/StatsGrid children (StatCards)
FilterBarMoléculamolecules/FilterBar children (filtros)
StatCardMoléculamolecules/StatCard label, value, sub
IslandBasePlantillaIslandBase.tsxmodule, title, stats, children