sapenlinea-components
v0.13.101
Published
Esta librería incluye una colección de componentes reutilizables para formularios, tablas, filtros, dashboards, gestión de menús/sidebar y más. A continuación se documenta **qué hace cada componente** y **cómo se utiliza**.
Readme
Componentes de la librería sapenlinea-components
Esta librería incluye una colección de componentes reutilizables para formularios, tablas, filtros, dashboards, gestión de menús/sidebar y más. A continuación se documenta qué hace cada componente y cómo se utiliza.
Nota: todos los componentes son standalone de Angular, por lo que se importan directamente en el
importsdel componente donde se usen.
Sistema de iconos
La librería unifica dos fuentes de iconos con el mismo formato de valor en tabla, module-card, icon-picker y formularios:
| Tipo | Formato | Ejemplo |
|---|---|---|
| CSS (icons.css) | clase icon-* | icon-edit, icon-shield |
| Lucide (lucide-angular) | prefijo lucide: + nombre kebab-case | lucide:share-2, lucide:users |
Utilidades exportadas desde sapenlinea-components (vía IconPicker):
import {
isLucideIconValue,
parseIconPickerValue,
resolveIconKind,
getLucideIconName,
getCssIconClass,
CSS_ICON_CATALOG,
DEFAULT_MODULE_ICONS,
} from 'sapenlinea-components';
iconType: 'lucide'enTableAction/ columnasicon-buttonsigue funcionando por retrocompatibilidad, pero está deprecado. Usar siemprelucide:nombre.
Peer dependency: lucide-angular (requerida para iconos Lucide).
Los iconos de acción fijos de module-card (editar, eliminar, flechas) están en los templates del componente y usan clases de icons.css. Los iconos identificadores de módulo/submódulo/aplicativo se configuran por datos (icon en ModuleData).
1. lib-date-time-filter (DateTimeFilter)
Qué hace
Componente para gestionar filtros de fecha y fecha/hora a partir de una lista de filtros configurables. Implementa ControlValueAccessor para integrarse con formularios reactivos.
Selector: lib-date-time-filter
Inputs:
filters: FilterItem[](requerido) – Filtros disponibles. CadaFilterItem:label: string– Texto visible en el chip/botón.value: string– Identificador interno del filtro.type: 'date' | 'datetime'– Tipo de filtro.placeholder?: string– Placeholder específico.minDate?: Date/maxDate?: Date– Rango permitido.
clearTrigger: number = 0– Al incrementar este valor desde el padre se limpia el filtro.initialValue: Date | string | null = null– Fecha a restaurar al inicializar (útil para volver a una pantalla con filtros previos). Al hacer clic en el botón de limpiar del chip, el valor inicial queda descartado y no se restaura automáticamente.initialFilterType: string | null = null– Key (FilterItem.value) del filtro activo a restaurar junto coninitialValue.
Outputs:
dateSelected: EventEmitter<DateFilterSelection>– Emite{ filter: string; value: Date }con el filtro activo y la fecha seleccionada.dateChange: EventEmitter<Date | null>– Emite la fecha seleccionada al cambiar (onullsi se limpia).enterPressed: EventEmitter<void>– Emite al presionar Enter.
Uso con formularios reactivos:
<lib-date-time-filter
[filters]="dateFilters"
[clearTrigger]="clearVersion"
[initialValue]="savedFilters.date"
[initialFilterType]="'date'"
(dateSelected)="onDateSelected($event)"
(dateChange)="onDateChange($event)"
></lib-date-time-filter>dateFilters: FilterItem[] = [
{ label: 'Fecha inicio', value: 'startDate', type: 'date', placeholder: 'Seleccionar fecha inicio' },
{ label: 'Fecha fin', value: 'endDate', type: 'date', placeholder: 'Seleccionar fecha fin' },
];2. lib-date-time-picker (DateTimePicker)
Qué hace
Componente de selector de fecha o fecha/hora independiente (sin lista de filtros). Implementa ControlValueAccessor.
Selector: lib-date-time-picker
Inputs:
mode: 'date' | 'datetime' = 'date'– Modo fecha sola o fecha + hora.placeholder: string = 'Seleccionar fecha'– Texto cuando no hay valor.minDate: Date | null = null– Fecha mínima.maxDate: Date | null = null– Fecha máxima.
Outputs:
dateChange: EventEmitter<Date | null>– Emite la fecha seleccionada.
Comportamiento del calendario:
- El panel se renderiza con
position: fixedy se adjunta adocument.bodypara quedar por encima de modales y contenedores con scroll. - Se posiciona preferentemente hacia abajo si hay espacio; solo abre hacia arriba cuando no cabe abajo.
- Se reposiciona automáticamente en scroll y resize.
Ejemplo de uso:
<lib-date-time-picker
mode="datetime"
[minDate]="minDate"
[maxDate]="maxDate"
(dateChange)="onDateChange($event)"
formControlName="fechaEvento"
></lib-date-time-picker>3. lib-dialog-alert-component (DialogAlertComponent)
Qué hace
Componente de diálogo/modal de alerta/confirmación, con opción de exigir una razón (select + textarea) antes de confirmar.
Selector: lib-dialog-alert-component
Inputs:
title: string = 'Mensaje'– Título del diálogo.message: string– Mensaje principal.type: 'warning' | 'error' | 'info' | 'success' = 'info'– Tipo visual de alerta.action?: string– Acción asociada (ej.'deactivate','delete','anulate','sancionar','active').showReason: boolean = false– Forzar mostrar campo de razón.confirm: boolean = false– Indica si muestra botón de confirmación principal.confirmLabel: string = 'Aceptar'– Texto del botón de confirmación.reasonOptions: { label: string; value: any }[] = []– Opciones para el campo "Razón" cuando se requiere.
Outputs:
close: EventEmitter<boolean>– Emitetrueal confirmar yfalseal cancelar.confirmReason: EventEmitter<string | undefined>– Emite la razón seleccionada (oundefined).
Ejemplo de uso:
<lib-dialog-alert-component
[title]="'Desactivar usuario'"
[message]="'¿Está seguro que desea desactivar este usuario?'"
type="warning"
action="deactivate"
[showReason]="true"
[reasonOptions]="reasonOptions"
(close)="onClose($event)"
(confirmReason)="onConfirmReason($event)"
></lib-dialog-alert-component>4. lib-dynamic-form-fields (DynamicFormFields)
Qué hace
Componente que renderiza dinámicamente campos de formulario a partir de una configuración (sections y fields), soportando textos, números, selects, checkboxes, radios, toggles, textarea, fechas y más. Maneja lógicas de copiado automático entre campos.
Selector: lib-dynamic-form-fields
Inputs:
form: FormGroup(requerido) – Formulario reactivo que contiene los controles.sections: SectionConfig[] = []– Secciones y campos del formulario.compact: boolean = false– Permite estilos compactos.
Tipos principales:
export interface Option {
value: string | number | boolean;
label: string;
subtitle?: string; // Texto secundario opcional bajo el label (solo checkbox)
}
export interface FieldConfig {
key: string;
label?: string;
type: 'text' | 'email' | 'number' | 'date' | 'datetime-local' | 'select'
| 'radio' | 'checkbox' | 'textarea' | 'disabled' | 'password'
| 'time' | 'file' | 'toggle';
placeholder?: string;
required?: boolean;
options?: Option[];
col?: number; // Columnas en el grid de 12 (default 6). No aplica a toggle.
disabled?: boolean;
readonly?: boolean;
matchWith?: string;
copyFrom?: string;
pattern?: string;
patternType?: 'numbers' | 'phone' | 'custom' | 'text' | 'username' | 'alphanumeric';
minDate?: Date;
maxDate?: Date;
variant?: 'cards'; // Variante visual del checkbox
uppercase?: boolean; // false desactiva mayúsculas automáticas en text/number/time y en labels de checkbox
radioGroup?: string; // Ver sección "Checkbox — comportamientos de selección"
atLeastOneGroup?: string;
}
export interface SectionConfig {
title?: string;
description?: string;
fields: FieldConfig[];
}Comportamiento del layout:
| Tipo | Comportamiento en el grid |
|---|---|
| text, number, email... | Respeta col (default 6 de 12) |
| radio, checkbox | Respeta col (default 6 de 12) |
| toggle | Siempre ocupa la fila completa. Hasta 4 toggles consecutivos comparten la misma fila (cada uno ocupa 1/4 del ancho). Nunca comparte fila con otros tipos. |
Checkbox — subtítulo y mayúsculas:
// Checkbox: uppercase controla las etiquetas de las opciones (default: mayúsculas)
{
key: 'acepta',
type: 'checkbox',
uppercase: false,
options: [{ label: 'Evidencia fotográfica', value: 'foto', subtitle: 'Adjunta imágenes' }]
}
// Text / number / time: uppercase: false evita convertir el valor a mayúsculas al escribir
{ key: 'placa', type: 'text', uppercase: false, placeholder: 'ABC123' }Checkbox — disabled vs readonly
Ambas propiedades bloquean la interacción, pero con apariencia diferente:
| Propiedad | Opacidad | Checkmark si true | Interacción |
|---|---|---|---|
| ninguna | 100% | Verde | Sí |
| readonly: true | 60% | Verde preservado | No |
| disabled: true | 50% | Gris (oculto) | No |
Usar readonly cuando el valor debe ser visible y reconocible pero no editable (ej. un checkbox ya confirmado). Usar disabled cuando el campo no aplica en el contexto actual.
// Checkbox visible y marcado, solo lectura
{ key: 'confirmado', type: 'checkbox', variant: 'cards', readonly: true, options: [...] }
// Checkbox no disponible en este contexto
{ key: 'premium', type: 'checkbox', variant: 'cards', disabled: true, options: [...] }Ambas propiedades funcionan tanto para un field con varias opciones como para fields de una sola opción.
Checkbox — comportamientos de selección entre fields
Cuando cada FieldConfig de tipo checkbox tiene una sola opción, se pueden agrupar fields con comportamiento especial usando radioGroup o atLeastOneGroup.
Ambas propiedades buscan a través de todas las secciones pasadas al componente, no solo la sección actual.
radioGroup — exclusión mutua: al marcar un field se desmarcan todos los demás del mismo grupo (comportamiento radio).
// FormGroup: cada key es un FormArray de un solo control
form = new FormGroup({
evidencia: new FormArray([new FormControl(true)]),
incidente: new FormArray([new FormControl(false)]),
accidente: new FormArray([new FormControl(false)]),
});
sections: SectionConfig[] = [{
title: 'Tipo de reporte',
fields: [
{ key: 'evidencia', type: 'checkbox', variant: 'cards', radioGroup: 'tipoReporte', col: 4, options: [{ label: 'Evidencia pedagógica', value: 'evidencia' }] },
{ key: 'incidente', type: 'checkbox', variant: 'cards', radioGroup: 'tipoReporte', col: 4, options: [{ label: 'Incidente vial', value: 'incidente' }] },
{ key: 'accidente', type: 'checkbox', variant: 'cards', radioGroup: 'tipoReporte', col: 4, options: [{ label: 'Accidente', value: 'accidente' }] },
],
}];atLeastOneGroup — mínimo uno activo: permite seleccionar varios, pero impide desmarcar el último activo del grupo.
form = new FormGroup({
foto: new FormArray([new FormControl(true)]),
video: new FormArray([new FormControl(false)]),
documento: new FormArray([new FormControl(false)]),
});
sections: SectionConfig[] = [{
title: 'Evidencias requeridas',
fields: [
{ key: 'foto', type: 'checkbox', variant: 'cards', atLeastOneGroup: 'evidencia', col: 4, options: [{ label: 'Foto', value: 'foto' }] },
{ key: 'video', type: 'checkbox', variant: 'cards', atLeastOneGroup: 'evidencia', col: 4, options: [{ label: 'Video', value: 'video' }] },
{ key: 'documento', type: 'checkbox', variant: 'cards', atLeastOneGroup: 'evidencia', col: 4, options: [{ label: 'Documento', value: 'documento' }] },
],
}];
radioGroupyatLeastOneGroupson independientes entre sí. Se pueden usar en diferentesSectionConfigdel mismo formulario sin interferencia.
Ejemplo de uso:
<lib-dynamic-form-fields
[form]="form"
[sections]="sections"
[compact]="true"
></lib-dynamic-form-fields>5. lib-input-text-filter (InputTextFilter)
Qué hace
Componente de filtro por texto con chips de filtros configurables (por ejemplo, buscar por nombre, documento, etc.). Implementa ControlValueAccessor.
Selector: lib-input-text-filter
Inputs:
filters: FilterItem[](requerido) – Filtros disponibles de texto.clearTrigger: number = 0– Al incrementar este valor desde el padre se limpian todos los filtros.initialValues: Record<string, string> = {}– Valores a restaurar al inicializar, donde la clave es elFilterItem.valuey el valor es el texto. Al limpiar un chip individualmente el valor inicial queda descartado y no se restaura automáticamente.
Outputs:
filterSelected: EventEmitter<{ filter: string; value: string }>– Emite el filtro y el valor aplicado.valueChange: EventEmitter<string | null>– Emite el valor actual del filtro de texto.enterPressed: EventEmitter<void>– Emite al presionar Enter.
Ejemplo de uso:
<lib-input-text-filter
[filters]="textFilters"
[clearTrigger]="clearVersion"
[initialValues]="{ name: 'Juan', email: '[email protected]' }"
(filterSelected)="onTextFilter($event)"
(valueChange)="onTextValueChange($event)"
></lib-input-text-filter>6. lib-input-number-filter (InputNumberFilter)
Qué hace
Componente de filtro numérico con múltiples chips (ej. "monto mínimo", "monto máximo"). Implementa ControlValueAccessor.
Selector: lib-input-number-filter
Inputs:
filters: FilterItem[](requerido) – Filtros numéricos.clearTrigger: number = 0– Limpia todos los filtros al incrementar.initialValues: Record<string, string> = {}– Valores numéricos a restaurar al inicializar, donde la clave es elFilterItem.value. Al limpiar un chip individualmente el valor inicial queda descartado y no se restaura automáticamente.
Outputs:
filterSelected: EventEmitter<{ filter: string; value: string }>– Filtro seleccionado y valor.valueChange: EventEmitter<string | null>– Valor numérico aplicado.enterPressed: EventEmitter<void>– Emite al presionar Enter.
Ejemplo de uso:
<lib-input-number-filter
[filters]="numberFilters"
[clearTrigger]="clearVersion"
[initialValues]="{ id: '42' }"
(filterSelected)="onNumberFilter($event)"
></lib-input-number-filter>7. lib-input-select-filter (InputSelectFilter)
Qué hace
Componente de filtro basado en select con chips. Permite seleccionar una opción por cada tipo de filtro configurado.
Selector: lib-input-select-filter
Inputs:
filters: FilterItem[](requerido) – Deben incluiroptionscon{ label, value }.clearTrigger: number = 0– Limpia todos los filtros al incrementar.initialValues: Record<string, string> = {}– Valores a restaurar al inicializar, donde la clave es elFilterItem.valuey el valor es el value de la opción (no el label). El componente busca automáticamente el label correspondiente para mostrarlo en el chip. Al limpiar un chip individualmente el valor inicial queda descartado y no se restaura automáticamente.
Outputs:
filterSelected: EventEmitter<{ filter: string; value: string }>– Filtro e identificador de opción seleccionada.valueChange: EventEmitter<string | null>– Valor del select actual.enterPressed: EventEmitter<void>– Emite al presionar Enter.
Ejemplo de uso:
<lib-input-select-filter
[filters]="statusFilters"
[clearTrigger]="clearVersion"
[initialValues]="{ status: 'ACTIVE' }"
(filterSelected)="onStatusFilter($event)"
></lib-input-select-filter>
initialValuesrecibe el value de la opción (ej.'ACTIVE'), no el label ('Activo'). El componente resuelve el label buscando en lasoptionsdel filtro correspondiente.
8. lib-select-custom-search (SelectCustomSearch)
Qué hace
Componente de select con buscador y filtrado en tiempo real sobre las opciones. Implementa ControlValueAccessor.
Selector: lib-select-custom-search
Inputs:
options: { value: any; label: string }[] = []– Opciones disponibles.placeholder: string = 'Seleccionar opción'– Texto cuando no hay selección.
Outputs:
selectionChange: EventEmitter<Option | null>– Emite la opción seleccionada (onull).
Ejemplo de uso:
<lib-select-custom-search
[options]="userOptions"
(selectionChange)="onUserSelected($event)"
formControlName="usuarioId"
></lib-select-custom-search>9. lib-pagination (PaginationComponent)
Qué hace
Componente de paginación para tablas o listas, con opción de selector de tamaño de página.
Selector: lib-pagination
Inputs:
page: number = 1– Página actual.pageSize: number = 10– Elementos por página (valor inicial).totalItems: number = 0– Total de elementos.showPageSizeSelector: boolean = false– Muestra/oculta el selector de tamaño de página.pageSizeOptions: number[] = [5, 10, 20, 50]– Opciones disponibles.
Outputs:
pageChange: EventEmitter<number>– Emite la nueva página seleccionada.pageSizeChange: EventEmitter<number>– Emite el nuevo tamaño de página.
Ejemplo de uso:
<lib-pagination
[page]="currentPage"
[pageSize]="pageSize"
[totalItems]="totalItems"
[showPageSizeSelector]="true"
[pageSizeOptions]="[10, 20, 50]"
(pageChange)="onPageChange($event)"
(pageSizeChange)="onPageSizeChange($event)"
></lib-pagination>10. lib-table (Table)
Qué hace Componente de tabla dinámica con soporte de ordenamiento, acciones por fila, filas expandibles, selección múltiple, totales, column groups, estados con tonos configurables y traducción de estados mediante enum.
Selector: lib-table
Inputs:
columns: TableColumn[](requerido) – Definición de columnas.data: TableRow[](requerido) – Datos a mostrar.actions: TableAction[] | ((row: TableRow) => TableAction[])– Acciones por fila principal.showActions: boolean = true– Muestra/oculta columna de acciones en filas principales.subColumns: SubColumn[]– Columnas de las sub-filas expandibles.subActions: TableAction[] | ((row: TableRow) => TableAction[])– Acciones por sub-fila.showSubActions: boolean = false– Muestra/oculta acciones en sub-filas.isRowExpandable: (row: TableRow) => boolean = () => false– Determina si una fila puede expandirse.expandedChildren: (row: TableRow) => TableRow[] | null = () => null– Proveedor de sub-filas al expandir.rowIdKey: string = 'id'– Propiedad del row usada como identificador para expansión y selección.showSelection: boolean = false– Muestra columna de checkboxes para selección múltiple.showTotals: boolean = false– Muestra fila de totales (columnasmoneycontotalizable: true).columnGroups: ColumnGroup[] = []– Grupos de columnas adicionales (checkbox, número, icon-button, porcentaje).statusToneMap: StatusToneMap– Mapa de estado normalizado → tono visual.statusEnumMap: Record<number | string, string>– Traduce el valor crudo del estado a la etiqueta visible.
Tipos de columna (type):
| Tipo | Descripción |
|---|---|
| text | Texto plano (default). Aplica autoWidth automáticamente. |
| number | Número formateado |
| money | Valor monetario con formato |
| date | Fecha formateada |
| datetime | Fecha y hora |
| percentage | Porcentaje |
| status | Chip de estado con color basado en statusToneMap |
| grade | Chip de grado con color semáforo automático (verde ≤0.9, naranja ≤2.9, rojo ≤4, gris >4). Muestra el valor con sufijo ° |
| icon-button | Botón con ícono clicable |
| email | Email en minúsculas. Aplica autoWidth automáticamente. |
Anchos de columna (automático + overrides):
La tabla calcula minWidth, maxWidth y perfil (compact | fixed | flex) según el tipo de columna y la longitud del label. Si el ancho mínimo total supera el contenedor, aparece scroll horizontal con barra personalizada (sin colapsar títulos).
| Tipo | Perfil por defecto | Comportamiento |
|---|---|---|
| icon-button, grade | compact | Ancho mínimo fijo |
| status, number, money, date, … | fixed | Rango min/max acotado |
| text, email | flex | Crece si hay espacio; ellipsis si no |
Overrides manuales (columnas principales y subColumns):
| Propiedad | Descripción |
|---|---|
| widthProfile | 'compact' \| 'fixed' \| 'flex' — fuerza el perfil |
| minWidth | Ancho mínimo en px |
| maxWidth | Ancho máximo en px |
| compact | Atajo a perfil compacto |
| autoWidth: false | Columna flexible principal (absorbe espacio libre) |
| headerMultiline | Fuerza salto de línea en el título del encabezado (white-space: pre-line) |
Encabezados multilínea: incluye \n en label (p. ej. 'N°\nCOMPARENDO') o define headerMultiline: true. La librería aplica el estilo sin ::ng-deep externo. El ancho mínimo se calcula con la línea más larga del título.
{ key: 'numero', label: 'N°\nCOMPARENDO', align: 'center' }
// o explícito:
{ key: 'fecha', label: 'Fecha de\nexpedición', headerMultiline: true }
// Columna principal de descripción
{ key: 'descripcion', label: 'DESCRIPCIÓN', type: 'text', autoWidth: false }
// Subcolumna con la misma lógica automática
subColumns: SubColumn[] = [
{ key: 'evento', label: 'Evento', type: 'text' },
{ key: 'hora', label: 'Hora', type: 'datetime' },
];
// Override manual
{ key: 'placa', label: 'PLACA', type: 'text', widthProfile: 'fixed', maxWidth: 120 }Column groups (columnGroups):
Grupos de columnas adicionales al final de la tabla (anexos, checkboxes, acciones inline). Mantienen celdas estrechas fijas (~56px) sin configuración adicional.
export interface ColumnGroup {
groupLabel: string;
columns: ColumnGroupItem[];
}
export interface ColumnGroupItem {
key: string;
label: string;
description: string;
type?: 'checkbox' | 'number' | 'icon-button' | 'percentage';
icon?: string;
iconAlternate?: string;
iconActiveKey?: string;
isIconActive?: (row: TableRow) => boolean;
disabledKey?: string;
isIconDisabled?: (row: TableRow) => boolean;
iconColor?: string;
iconAlternateColor?: string;
/** Key del row con URL(s) del archivo a visualizar al hacer clic en checkbox marcado */
fileUrlKey?: string;
/** Key del row para el nombre del archivo en el visor (default: label de la columna) */
fileNameKey?: string;
}- Checkbox marcado con
fileUrlKey(o valor string en la celda) abre el visor PDF/imagen víaPdfViewerService. - Cambios en checkbox emiten
columnGroupChange.
Filas expandibles:
<lib-table
[columns]="columns"
[data]="rows"
[subColumns]="subColumns"
[isRowExpandable]="canExpand"
[expandedChildren]="getChildren"
rowIdKey="id"
/>canExpand = (row: TableRow) => !!row.hasChildren;
getChildren = (row: TableRow) => row.children ?? [];Selección múltiple (showSelection):
Activa checkboxes en encabezado y filas. El estado interno (selectedRows) se gestiona en el componente; expone métodos toggleRow, toggleAllRows, isRowSelected, isAllSelected vía la instancia.
Fila de totales (showTotals):
Suma automáticamente columnas con type: 'money' y totalizable: true.
Menú de acciones:
El panel contextual se renderiza con position: fixed adjunto a document.body, se reposiciona en scroll/resize y elige abrir hacia arriba o abajo según el espacio disponible.
Helpers exportados: resolveTableColumnLayout, getTableMinWidth, buildChildGridTemplateColumns, isMultilineHeaderLabel, ColumnWidthProfile, ResolvedTableColumnLayout.
Iconos en acciones y columnas icon-button:
Usar clases CSS o prefijo lucide: (ver sección Sistema de iconos al inicio del documento):
// Acción del menú contextual
actions: TableAction[] = [
{ key: 'edit', icon: 'icon-edit', label: 'Editar' },
{ key: 'share', icon: 'lucide:share-2', label: 'Compartir', color: '#006D2F' },
];
// Columna icon-button con estado alterno
{
key: 'download',
label: 'Documento',
type: 'icon-button',
icon: 'icon-file',
iconAlternate: 'icon-check',
iconActiveKey: 'downloadComplete',
iconAlternateColor: '#006D2F',
disabledKey: 'locked',
}Propiedades de columna icon-button: icon, iconAlternate, iconActiveKey, isIconActive, disabledKey, isIconDisabled, iconColor, iconAlternateColor. También aplican en columnGroups con type: 'icon-button'.
Outputs:
optionSelected: EventEmitter<{ action: string; row: TableRow }>– Emite la acción seleccionada y la fila principal.subOptionSelected: EventEmitter<{ action: string; row: TableRow }>– Emite la acción seleccionada y la sub-fila.iconAction: EventEmitter<{ action: string; row: TableRow }>– Emite al hacer clic en unicon-button(columnas o column groups).columnGroupChange: EventEmitter<{ key: string; row: TableRow; value: any }>– Emite al cambiar un valor encolumnGroups(p. ej. checkbox).
Sub-filas con acciones (subActions + showSubActions):
subActions: TableAction[] = [
{ key: 'view', icon: 'icon-view', label: 'Ver detalle' },
{ key: 'edit', icon: 'icon-edit', label: 'Editar' },
];<lib-table
[columns]="columns"
[data]="rows"
[subColumns]="subColumns"
[subActions]="subActions"
[showSubActions]="true"
(subOptionSelected)="onSubAction($event)"
/>Ejemplo con columna grade:
columns: TableColumn[] = [
{ key: 'numero', label: 'N° COMPARENDO', align: 'center' },
{ key: 'fecha', label: 'FECHA', align: 'center', type: 'date' },
{ key: 'grado', label: 'GRADO', align: 'center', type: 'grade' },
{ key: 'medicion', label: 'MEDICIÓN', align: 'center' }
];Uso con estados en inglés (enum):
// Enum que refleja los valores que devuelve el backend
export enum UserStatus {
ACTIVE = 'ACTIVE',
INACTIVE = 'INACTIVE',
PENDING = 'PENDING',
CANCELLED = 'CANCELLED',
}
// Traducción enum → etiqueta visible
statusEnumMap: Record<string, string> = {
[UserStatus.ACTIVE]: 'Activo',
[UserStatus.INACTIVE]: 'Inactivo',
[UserStatus.PENDING]: 'Pendiente',
[UserStatus.CANCELLED]: 'Cancelado',
};
// Tono visual usando la etiqueta normalizada (minúsculas)
statusToneMap: StatusToneMap = {
activo: 'success',
inactivo: 'neutral',
pendiente: 'warning',
cancelado: 'error',
};<lib-table
[columns]="columns"
[data]="rows"
[actions]="rowActions"
[statusToneMap]="statusToneMap"
[statusEnumMap]="statusEnumMap"
(optionSelected)="onTableAction($event)"
></lib-table>11. lib-modal-form (ModalForm)
Qué hace
Componente de modal genérico de formulario multi-paso, que delega la navegación de pasos a lib-wizard-form.
Selector: lib-modal-form
Inputs:
title: string = 'Formulario'– Título del modal.submitLabel: string = 'Guardar'– Texto del botón de guardar.form: FormGroup– Formulario reactivo principal.steps: ModalFormStep[] = []– Pasos del formulario.
export interface ModalFormStep {
key: string; // Nombre del subgrupo dentro de form
label: string; // Texto visible del paso
component: Type<any>; // Componente que se renderiza en ese paso
}Outputs:
onSubmit: EventEmitter<any>– Emite el valor completo del formulario.onCancel: EventEmitter<void>– Emite cuando se cancela el modal.
Ejemplo de uso:
<lib-modal-form
[title]="'Registro de usuario'"
[submitLabel]="'Guardar'"
[form]="form"
[steps]="steps"
(onSubmit)="onSubmit($event)"
(onCancel)="onCancel()"
></lib-modal-form>12. lib-wizard-form (WizardForm)
Qué hace
Componente que orquesta la lógica de pasos de un formulario tipo wizard, validando el sub-formulario del paso actual y notificando si se puede continuar.
Selector: lib-wizard-form
Inputs:
form: FormGroup– Formulario principal que contiene subgrupos.currentStep: number = 1– Paso actual (1-based).steps: ModalFormStep[] = []– Definición de los pasos.
Outputs:
canContinue: EventEmitter<boolean>– Emite si el grupo del paso actual es válido.
Uso habitual (interno de lib-modal-form):
No suele consumirse directamente, pero puede utilizarse si se quiere un wizard sin modal.
13. lib-input (Input)
Qué hace
Campo de entrada reutilizable con modos search y select, usado para buscadores simples con dropdown de opciones.
Selector: lib-input
Inputs principales:
mode: 'search' | 'select' = 'search'placeholder?: stringtype: string = 'text'options?: { code: string; name: string }[](modoselect)
Outputs principales:
valueChange: EventEmitter<string>onSearch: EventEmitter<string>onSelect: EventEmitter<string>
14. lib-dialog-confirmation (DialogConfirmation)
Qué hace
Modal ligero de confirmación con lista de ítems informativos y botón de aceptar.
Selector: lib-dialog-confirmation
Inputs principales:
title: stringconfirm: booleanconfirmLabel: stringitems: DialogItem[]– Lista de ítems con posibles hijos expandibles.
Outputs:
close: EventEmitter<boolean>–trueal confirmar,falseal cerrar sin confirmar.
15. lib-not-found-modal (NotFoundModal)
Qué hace
Modal genérico para mostrar estados de “no hay resultados / no encontrado” con acción de cierre.
Selector: lib-not-found-modal
Outputs:
onClose: EventEmitter<void>
16. lib-loader (Loader)
Qué hace
Indicador de carga centrado para usar como overlay dentro de contenedores o modales.
Selector: lib-loader
17. lib-progress-bar y ProgressFormService
Qué hacenlib-progress-bar muestra el avance de un flujo (por ejemplo, formularios multi-paso); ProgressFormService expone señales para currentPage y totalPages.
Selector: lib-progress-bar
Uso típico: inyectar ProgressFormService en componentes que actualizan el progreso.
18. lib-processing-overlay (ProcessingOverlay)
Qué hace
Overlay de pantalla completa o contenedor con spinner y texto (“Cargando…”) para procesos en curso.
Selector: lib-processing-overlay
Inputs:
loadingText: string = 'Cargando'
19. lib-info-group (InfoGroup)
Qué hace Muestra bloques de información clave (label + value) agrupados en dos columnas, útil para pantallas de detalle o resúmenes de entidad.
Selector: lib-info-group
El componente tiene tres modos de uso: info (grid de dos columnas), comparación (columnas independientes) y box (tarjeta de persona).
Inputs:
| Input | Tipo | Default | Descripción |
|---|---|---|---|
| title | string \| null | null | Título visible sobre el contenido |
| titleAlign | 'center' \| 'left' \| 'right' | 'center' | Alineación del título |
| items | InfoItem[] | [] | Modo info: items distribuidos en grid de 2 columnas |
| itemsLeft | InfoItem[] | [] | Modo comparación: items de la columna izquierda |
| itemsRight | InfoItem[] | [] | Modo comparación: items de la columna derecha |
| titleLeft | string \| null | null | Encabezado de la columna izquierda (sin divider) |
| titleRight | string \| null | null | Encabezado de la columna derecha (sin divider) |
| showBox | boolean | false | Activa modo tarjeta de persona |
| type | 'success' \| 'error' \| 'info' | 'info' | Color del modo showBox |
| fullName | string \| null | null | Nombre completo (modo showBox) |
| typeDocument | string \| null | null | Tipo de documento (modo showBox) |
| document | string \| null | null | Número de documento (modo showBox) |
El modo comparación se activa automáticamente cuando se pasa
itemsLeftoitemsRight. Si ninguno tiene valores, se usa el modo info (items).
Modo info — grid de dos columnas:
<lib-info-group
title="Datos del usuario"
[titleAlign]="'left'"
[items]="infoItems"
/>infoItems: InfoItem[] = [
{ label: 'Nombre', value: 'Julian Pérez' },
{ label: 'Estado', value: 'Activo' },
{ label: 'Email', value: '[email protected]' },
{ label: 'Rol', value: 'Administrador' },
];Modo comparación — columnas independientes:
Útil para comparar datos actuales vs nuevos, o dos entidades distintas. Cada columna tiene sus propios items y título opcional.
<lib-info-group
title="Comparación de datos"
[itemsLeft]="datosActuales"
[itemsRight]="datosNuevos"
titleLeft="Datos actuales"
titleRight="Datos nuevos"
/>datosActuales: InfoItem[] = [
{ label: 'Nombre', value: 'Julian Fernando' },
{ label: 'Apellido', value: 'Pérez García' },
{ label: 'Documento', value: '1020304050' },
];
datosNuevos: InfoItem[] = [
{ label: 'Nombre', value: 'Julián Andrés' },
{ label: 'Apellido', value: 'Pérez Rodríguez' },
{ label: 'Documento', value: '9876543210' },
];Modo box — tarjeta de persona:
<lib-info-group
[showBox]="true"
type="success"
[fullName]="persona.nombre"
[typeDocument]="persona.tipoDoc"
[document]="persona.documento"
/>20. lib-feature-card (FeatureCard)
Qué hace
Tarjeta compacta para resaltar una “feature” o métrica con icono, título y descripción/valor.
Selector: lib-feature-card
Inputs:
feature: Feature–{ icon: string; title: string; description: string }
21. lib-side-card y lib-side-card-detail
Qué hacen
Tarjetas laterales para flujos con mapa o dashboards, mostrando secciones de información y acciones (sliders, botones de sección, estados).
Selectores: lib-side-card, lib-side-card-detail
Inputs principales:
sections: SideCardSection[]statusValue: 1 | 2
Outputs típicos:
statusChange,tabChange,sectionButtonClick, etc. según el componente.
22. lib-devices-carousel (DevicesCarousel)
Qué hace
Carrusel horizontal de tarjetas de dispositivo con estado (batería, geocerca, conectividad).
Selector: lib-devices-carousel
Inputs:
devices: DeviceCard[]
Outputs:
btnClickIcon: EventEmitter<DeviceCard>– Al pulsar acción sobre un dispositivo.
23. lib-title-filters (TitleFilters)
Qué hace Encabezado de sección con título, botones de acción configurables y grupo de filtros (texto, número, select, fecha, datetime, hora).
Selector: lib-title-filters
buttonMode
| Valor | Descripción |
|---|---|
| 'toggle' | Botón "Filtros" que muestra/oculta el panel de filtros (default) |
| 'action' | Botón "Ir al Mapa" con ícono SVG fijo; emite filterButtonClicked |
| 'click' | Botón primario (lib-button) con ícono y texto configurables; emite filterButtonClicked |
| 'clickRight' | Igual que click, pero el botón primario se renderiza al final de la fila (junto a secundario/exportar) |
| 'none' | No muestra botón de acción/filtros en la barra del título |
Inputs:
title: string = 'Listado'filtersConfig: TitleFilterConfig[]buttonMode: 'toggle' | 'action' | 'click' | 'clickRight' | 'none' = 'toggle'clickButtonLabel: string = 'Acción'— texto del botón en modoclick/clickRightclickButtonIcon: string = ''— clase CSS del ícono en modoclick/clickRightalwaysShowFilters: boolean = false— muestra los filtros permanentemente sin necesidad de toggleshowSecondaryButton: boolean = false— muestra un botón secundario (ej. "Exportar PDF")secondaryButtonLabel: string = 'Exportar PDF'secondaryButtonIcon: string = 'icon-download'showButtonExport: boolean = false— muestra un botón de exportación adicional (ej. "Exportar CSV")exportButtonLabel: string = 'Exportar CSV'exportButtonIcon: string = 'icon-export'showButtonBack: boolean = falseinitialFilters: Record<string, string | number | Date | null> = {}— estado previo de filtros para restaurar al volver a la pantalla. La estructura es la misma que emitefiltersChange. Soporta todos los tipos de filtro: texto, número, select (por value), fecha, datetime y hora.
Los botones secundario y de exportación se agrupan automáticamente al final de la fila del título.
Restaurar filtros previos (initialFilters):
Patrón típico para preservar filtros al navegar entre pantallas:
// En el componente padre
savedFilters = signal<Record<string, string | number | Date | null>>({});
onFiltersChange(filters: Record<string, string | number | Date | null>) {
this.savedFilters.set(filters);
}<lib-title-filters
title="Comparendos"
[filtersConfig]="filtersConfig"
[initialFilters]="savedFilters()"
(filtersChange)="onFiltersChange($event)"
(applyFilters)="onApply()"
(clearFilters)="savedFilters.set({})"
/>Al hacer clic en "Borrar Filtros" todos los filtros se limpian, incluyendo los valores iniciales. Al limpiar un chip individual el valor inicial de ese filtro también queda descartado.
Outputs:
filtersChange: Record<string, string | number | Date | null>applyFilters: voidclearFilters: voidfilterButtonClicked: void— emitido al hacer clic en el botónclick,clickRightoactionsecondaryButtonClicked: voidexportButtonClicked: voidclickButtonBack: void
Íconos disponibles (clase CSS, patrón mask-image):
| Clase | Descripción |
|---|---|
| icon-download | Descargar |
| icon-upload | Cargar / subir |
| icon-export | Exportar tabla / exportar CSV |
| icon-add | Agregar / nuevo registro |
Ejemplo — con botón secundario y botón de exportar:
<lib-title-filters
title="Comparendos"
[filtersConfig]="filtersConfig"
[showSecondaryButton]="true"
secondaryButtonLabel="Exportar PDF"
secondaryButtonIcon="icon-download"
(secondaryButtonClicked)="onExportPDF()"
[showButtonExport]="true"
exportButtonLabel="Exportar CSV"
exportButtonIcon="icon-export"
(exportButtonClicked)="onExportCSV()"
(filtersChange)="onFiltersChange($event)"
(applyFilters)="onApply()"
/>TitleFilterConfig:
export interface TitleFilterConfig {
type: FilterType; // 'text' | 'number' | 'select' | 'date' | 'datetime' | 'time'
filters: FilterItem[];
}Cada entrada de filtersConfig renderiza un componente de filtro según su type:
| type | Componente interno |
|---|---|
| text | lib-input-text-filter |
| number | lib-input-number-filter |
| select | lib-input-select-filter |
| date / datetime | lib-date-time-filter |
| time | lib-input-time-filter |
Navegación con Enter: al presionar Enter en un filtro, el foco avanza al siguiente campo visible; en el último filtro se dispara applyFilters.
Ejemplo — modo clickRight con botón de acción alineado a la derecha:
<lib-title-filters
title="Reportes"
buttonMode="clickRight"
clickButtonLabel="Nuevo reporte"
clickButtonIcon="icon-add"
[showSecondaryButton]="true"
(filterButtonClicked)="onNuevoReporte()"
(secondaryButtonClicked)="onExportPDF()"
/>24. lib-notification-modal (NotificationModal)
Qué hace
Modal de notificación tipo “toast grande” centrado, para mensajes importantes con iconografía y botón de acción.
Selector: lib-notification-modal
25. lib-footer (Footer)
Qué hace
Footer compacto para pantallas o modales. Se ancla al borde inferior del layout flex (no usa position: fixed).
Selector: lib-footer
Inputs:
| Input | Tipo | Default | Descripción |
|---|---|---|---|
| backgroundColor | string | '#F0F0DB' | Fondo del footer |
| textColor | string | '#787861' | Color del texto |
| highlightColor | string | — | Color de marca/texto destacado |
| year | number | año actual | Año del copyright |
Layout recomendado — importar footer-layout.css y colocar el footer fuera del área con scroll:
<div class="footer-layout">
<main class="footer-layout__content">...</main>
<lib-footer [highlightColor]="'#596300'" />
</div>// En styles del proyecto o global
@import 'sapenlinea-components/lib/components/footer/footer-layout.css';26. lib-button-cards (ButtonCards)
Qué hace
Botón estilizado como tarjeta seleccionable, usado para opciones tipo “cards” (ej. selección de variante).
Selector: lib-button-cards
Inputs principales:
label: stringsubtitle?: stringvariant?: 'primary' | 'danger' | ...
Outputs:
selectedChange: EventEmitter<boolean>
27. lib-tabs (Tabs)
Qué hace
Componente de pestañas horizontales. Puede usarse solo como barra de navegación (el padre maneja el contenido) o en modo automático, renderizando lib-card-content o lib-info-group por pestaña según la configuración.
Selector: lib-tabs
Inputs:
tabs: TabItem[]activeTab: TabId | nullvalidateOnTabChange: boolean = false– Bloquea el cambio de tab si el formulario activo tiene campos inválidos.
Outputs:
tabChange: TabIdactiveTabChange: TabIdtabValidationFailed: TabId– Emite el id del tab actual cuando la validación impide el cambio.
TabItem:
export interface TabItem {
id: TabId; // string | number
label: string;
disabled?: boolean;
cards?: TabCardConfig[]; // si se define, el panel se renderiza automáticamente
}TabCardConfig — todas las propiedades son opcionales excepto title:
export interface TabCardConfig {
title: string;
// --- Modo formulario (lib-card-content) ---
isForm?: boolean;
form?: FormGroup;
sections?: SectionConfig[];
variant?: 'default' | 'numeric';
sectionNumber?: number | null;
bgColor?: 'default' | 'light' | string;
withSearch?: boolean;
searchPlaceholder?: string;
onSearch?: (value: string) => void;
onSearchClear?: () => void;
// --- Modo informativo (lib-info-group) ---
// Se activa cuando se define infoItems, infoItemsLeft o infoItemsRight
infoItems?: InfoItem[]; // grid de dos columnas (modo info)
infoTitleAlign?: 'center' | 'left' | 'right'; // default 'center'
infoItemsLeft?: InfoItem[]; // columna izquierda (modo comparación)
infoItemsRight?: InfoItem[]; // columna derecha (modo comparación)
infoTitleLeft?: string; // encabezado columna izquierda
infoTitleRight?: string; // encabezado columna derecha
}Modo formulario (comportamiento existente, sin cambios):
tabs: TabItem[] = [
{
id: 'datos',
label: 'Datos',
cards: [{ title: 'Información', isForm: true, form: this.form, sections: this.sections }]
}
];Modo informativo — grid de dos columnas:
tabs: TabItem[] = [
{
id: 'detalle',
label: 'Detalle',
cards: [
{
title: 'Información General',
infoItems: [
{ label: 'Nombre', value: 'Julian Pérez' },
{ label: 'Estado', value: 'Activo' },
{ label: 'Email', value: '[email protected]' },
{ label: 'Rol', value: 'Administrador' },
],
infoTitleAlign: 'left',
}
]
}
];Modo comparación — columnas independientes:
tabs: TabItem[] = [
{
id: 'comparacion',
label: 'Comparación',
cards: [
{
title: 'Datos del usuario',
infoItemsLeft: [
{ label: 'Nombre', value: 'Julian Fernando' },
{ label: 'Documento', value: '1020304050' },
{ label: 'Estado', value: 'Activo' },
],
infoItemsRight: [
{ label: 'Nombre', value: 'Julián Andrés' },
{ label: 'Documento', value: '9876543210' },
{ label: 'Estado', value: 'Inactivo' },
],
infoTitleLeft: 'Datos actuales',
infoTitleRight: 'Datos nuevos',
}
]
}
];<lib-tabs [tabs]="tabs" />Los cuatro modos (barra sin
cards, formulario, informativo, comparación) son totalmente compatibles y se pueden mezclar en el mismo componente.
28. lib-not-found-section (NotFoundSection)
Qué hace
Sección de “no hay datos” para usar dentro de vistas (en vez de modal), con botón opcional para crear/recargar.
Selector: lib-not-found-section
Inputs típicos:
buttonLabel?: string
Outputs:
openModal: EventEmitter<void>
29. lib-toast y ToastService
Qué hacen
Sistema de notificaciones tipo toast:
lib-toast: componente visual que muestra la cola de toasts.ToastService: servicio para disparar toasts (success,error,info, etc.).
Uso típico:
- Declarar
<lib-toast></lib-toast>una sola vez (por ejemplo enAppComponent). - Inyectar
ToastServicedonde se necesiten notificaciones.
30. lib-card-content (CardContent)
Qué hace
Card contenedora de secciones de formulario o contenido libre (ng-content), con header configurable, barra de búsqueda integrada y color de fondo personalizable.
Selector: lib-card-content
Inputs:
| Input | Tipo | Default | Descripción |
|---|---|---|---|
| title | string | 'Título de sección' | Título del header |
| variant | 'default' \| 'numeric' | 'default' | Estilo del header |
| sectionNumber | number \| null | null | Número en ícono circular (variant='numeric') |
| isForm | boolean | false | Renderiza lib-dynamic-form-fields internamente |
| form | FormGroup | — | Formulario reactivo (requiere isForm=true) |
| sections | SectionConfig[] | [] | Secciones del formulario |
| bgColor | 'default' \| 'light' \| string | 'default' | Color de fondo (preset o valor CSS libre) |
| withSearch | boolean | false | Muestra barra de búsqueda (ocupa 50% del ancho) |
| searchPlaceholder | string | 'Buscar...' | Placeholder del input de búsqueda |
| searchValue | string (model) | '' | Valor del input — soporta [(searchValue)] |
Outputs:
onSearch: string— emitido al presionar Enter o el botón buscaronSearchClear: void— emitido al limpiar el input
Colores de fondo (bgColor):
import { CARD_BG } from 'sapenlinea-components';
// Presets disponibles
CARD_BG.default // '#EBE8D6' (beige, default)
CARD_BG.light // '#ededdf' (más claro)
// O cualquier valor CSS
bgColor = '#FFFFFF';
bgColor = 'rgb(235, 232, 214)';El componente sincroniza automáticamente
backgroundy--sl-form-surfacepara que los labels flotantes del formulario siempre coincidan con el fondo.
Ejemplo — formulario con búsqueda y color custom:
<lib-card-content
title="Usuarios"
variant="numeric"
[sectionNumber]="1"
[isForm]="true"
[form]="userForm"
[sections]="userSections"
bgColor="light"
[withSearch]="true"
searchPlaceholder="Buscar usuario..."
[(searchValue)]="searchText"
(onSearch)="buscar($event)"
(onSearchClear)="limpiar()"
/>Ejemplo — contenido libre:
<lib-card-content title="Resumen" bgColor="#f5f5f0">
<p>Cualquier contenido aquí</p>
</lib-card-content>31. lib-button (Button)
Qué hace
Botón unificado de la librería con variantes de color y tamaños consistentes. Reemplaza los estilos de botones sueltos en modal-form y otros componentes.
Selector: lib-button
Inputs:
variant: 'primary' | 'secondary' | 'outline' = 'primary'type: 'button' | 'submit' = 'button'disabled: boolean = falsefullWidth: boolean = falsesize: 'default' | 'compact' = 'default'–defaultigual al botón demodal-form,compactmás pequeño.
Outputs:
clicked: EventEmitter<MouseEvent>
Ejemplo rápido:
<lib-button variant="primary" (clicked)="onSubmit()">
Guardar
</lib-button>
<lib-button variant="secondary" size="compact" (clicked)="onCancel()">
Cancelar
</lib-button>
<lib-button variant="outline" [fullWidth]="true" size="compact">
Acción secundaria
</lib-button>32. lib-toggle-custom (ToggleCustom)
Qué hace
Toggle (interruptor on/off) estilizado con label integrado. Implementa ControlValueAccessor para uso en formularios reactivos.
Selector: lib-toggle-custom
Inputs:
label: string = ''– Texto descriptivo que acompaña el toggle.
Uso standalone:
<lib-toggle-custom label="Recibir notificaciones" formControlName="notificar" />Uso en lib-dynamic-form-fields:
{
key: 'notificar',
type: 'toggle',
label: 'Recibir notificaciones',
}Los campos
togglesiempre ocupan la fila completa del formulario y nunca comparten fila con otros tipos de campo. Hasta 4 toggles consecutivos se distribuyen automáticamente en la misma fila (cada uno ocupa 1/4 del ancho).
33. lib-option-card (OptionCard)
Qué hace
Tarjeta interactiva estilizada con ícono para presentar opciones seleccionables, utilizada típicamente en menús o modales. Permite personalizar el estado visual y colores.
Selector: lib-option-card
Inputs:
label: string(requerido) – Título principal de la opción.description: string– Texto de acompañamiento secundario.buttonBg: string– Color de fondo CSS del cuerpo general de la tarjeta.iconBg: string– Color de fondo sólido del contenedor de ícono interno.
Outputs:
clickAction: EventEmitter<void>– Invocado al presionar la tarjeta completa.
Proyección:
Usa <svg icon ...></svg> (selector [icon]) para insertar un gráfico que adoptará de forma automática el color surface contrastante para una correcta legibilidad frente a iconBg.
34. lib-kpi-card (KpiCard)
Qué hace
Tarjeta informativa destinada a exponer Indicadores de Rendimiento (KPI) con el título, descripción, icono y valor métrico destacado.
Selector: lib-kpi-card
Inputs principales:
title: string– Cabecera del KPI.description: string– Metadato o guía visual inferior.value: string | number– Indicador numérico/texto del KPI.iconColor: "primary" | "secondary" | "tertiary" | "quaternary"– Asigna el esquema de colores/tonos automáticamente al ícono.
Proyección:
Usa el contenedor de <svg icon ...></svg> para alojar el identificador visual genérico del KPI.
35. lib-load-image (LoadImage)
Qué hace
Zona de arrastrar y soltar (Drag & Drop) para la recepción de imágenes de los usuarios, la cual permite la visualización in-place, borrado, carga manual y explorador interactivo modal en pantalla completa (lightbox estilo zoom).
Selector: lib-load-image
Inputs principales:
title: string = "Cargar firma"– Encabezado superior.description: string = "Formatos admitidos..."– Helper text inferior.titleIcon: string = ""– Renderiza una clase CSS particular de ícono sobre el título.iconUpload: boolean = false– Ajuste de la gráfica in-caja que induce a la subida de un archivo.imageUrl: string = ""– Permite inyectar y visualizar nativamente una imagen preexistente obtenida desde una URL externa (ideal para editar la entidad de un bucket).
Outputs:
imageLoaded: EventEmitter<File | null>– Entrega el archivo File procesado para subidas directas del usuario onullsi lo borró.
36. Gráficas y Visualización: ECharts (Charts)
Qué hacen
Subsistema de envolturas especializadas (wrappers) cimentado sobre apache/echarts para habilitar representaciones informativas sólidas a tamaño responsivo y dinámico. Incluye soporte para interactividad, visualizaciones jerárquicas, tablas expandibles y una paleta de 20 colores.
Componentes disponibles:
lib-bar-chart
Gráfica de barras estandarizada con soporte para una o múltiples series.
- Inputs:
data: ChartItem[]– Datos para gráfica de barra simple.multiSeries: BarSeries[]– (Opcional) Permite mostrar grupos de barras por cada sección.
- Interfaces:
export interface BarSeries { name: string; // Nombre de la serie (aparece en la leyenda) data: number[]; // Valores numéricos color?: string; // Color personalizado opcional }
lib-donut-chart
Gráfica de anillo con capacidad de Drill-down, navegación interna y leyenda automática.
- Inputs:
data: ChartItem[]– Datos granulares (ej. códigos de infracciónD01,D02,C01).drillDown: boolean = false– Activa el modo de exploración jerárquica.groupByPrefix: number = 0– Agrupa los datos por los primeros N caracteres de la etiqueta (ej.1para agrupar por letras iniciales).
- Comportamiento:
- Al activar el drill-down, el componente muestra inicialmente los totales agrupados y permite al usuario pulsar una sección para "abrirla" y ver su desglose, con un botón de "Volver" integrado.
- Cuando hay más de 10 ítems, los labels de las porciones se ocultan automáticamente y se muestra una leyenda horizontal scrollable en la parte inferior.
lib-table-chart
Tabla expandible integrada en el dashboard de gráficas, con paginación y estado "sin datos".
- Inputs:
title: string = 'Table Chart'– Título del encabezado.columns: TableColumn[]– Definición de columnas (misma interfaz quelib-table).data: TableRow[]– Datos de la tabla.totalItems: number = 0– Total de registros (para la paginación).itemsPerPage: number = 10– Registros por página en modo expandido.
- Comportamiento:
- Colapsado: Muestra los primeros 8 registros sin paginación.
- Expandido: Ocupa pantalla completa (90vw × 90vh) con overlay, muestra 10 registros por página con
lib-pagination. - Si
dataestá vacío muestralib-not-found-sectioncentrado.
Ejemplo:
<lib-table-chart
title="Tabla de Comparendos"
[columns]="columns"
[data]="comparendosData"
[totalItems]="comparendosData.length" />Otros:
lib-line-chart– Líneas analíticas en evolución.lib-heatmap– Mapa de concentración de calor basado en coordenadas / categorías.lib-map-geo– Puntos de calor sobre mapa geofísico interactivo.
Paleta de colores (20 colores): La paleta compartida por todas las gráficas incluye: olive green, lime soft, burgundy, deep teal, sage, chartreuse, sky blue, light steel blue, warm amber, terracotta, dusty purple, jade green, coral clay, charcoal slate, sand gold, mint sage, rose mauve, navy steel, peach y spring green.
Eventos Globales:
Todos los componentes de gráficas emiten el evento chartClick, permitiendo capturar clics en secciones específicas para lógicas personalizadas en el padre.
37. lib-quick-access-cards (QuickAccessCards)
Qué hace
Componente de accesos rápidos que renderiza un grid de tarjetas (lib-option-card en modo quick-access) con hasta 4 columnas por fila. Si hay más de 4 items, los restantes se posicionan en la siguiente fila. Los íconos se definen mediante un nombre de clase CSS en lugar de SVG inline, simplificando la configuración significativamente.
Selector: lib-quick-access-cards
Inputs:
| Input | Tipo | Default | Descripción |
|---|---|---|---|
| title | string | 'Accesos Rápidos' | Título visible sobre el grid de tarjetas |
| items | QuickAccessItem[] | [] | Lista de accesos rápidos a renderizar |
Outputs:
clickAction: EventEmitter<QuickAccessItem>– Emite el item completo al hacer clic en una tarjeta.
Interfaz:
export interface QuickAccessItem {
id: string;
label: string;
description: string;
icon: string; // Nombre de clase CSS del ícono, ej. 'icon-building'
iconBg?: string; // Color de fondo del contenedor del ícono
}Íconos disponibles:
| Clase | Descripción |
|---|---|
| icon-settings | Configuración / engranaje |
| icon-check | Check / verificado |
| icon-close | Cerrar / X |
| icon-info | Información |
| icon-alert | Alerta / triángulo |
| icon-star | Estrella |
| icon-users | Usuarios / personas |
| icon-building | Edificio / organismo |
| icon-car | Vehículo |
| icon-shield | Escudo / seguridad |
| icon-clipboard | Portapapeles |
| icon-map-pin | Ubicación / mapa |
| icon-chart | Gráfica de barras |
| icon-file | Documento / archivo |
| icon-refresh | Refrescar / recargar |
| icon-edit | Editar / lápiz |
| icon-search | Buscar / lupa |
Ejemplo de uso:
import { QuickAccessCards, QuickAccessItem } from 'sapenlinea-components';
quickAccessItems: QuickAccessItem[] = [
{ id: '1', label: 'Organismo de tránsito', description: 'Información del organismo', icon: 'icon-building', iconBg: '#00695c' },
{ id: '2', label: 'Usuarios', description: 'Administración de usuarios', icon: 'icon-users', iconBg: '#0d47a1' },
{ id: '3', label: 'Roles', description: 'Administración de roles', icon: 'icon-shield', iconBg: '#f57f17' },
{ id: '4', label: 'Reportes', description: 'Generación de reportes', icon: 'icon-chart', iconBg: '#b71c1c' },
];<lib-quick-access-cards
[title]="'Accesos Rápidos'"
[items]="quickAccessItems"
(clickAction)="onQuickAccess($event)" />Comportamiento responsive:
| Ancho de pantalla | Columnas | |---|---| | > 1200px | 4 columnas | | 900px – 1200px | 3 columnas | | 600px – 900px | 2 columnas | | < 600px | 1 columna |
38. lib-input-time-filter (InputTimeFilter)
Qué hace
Componente de filtro de hora con chips configurables y selector AM/PM desplegable. Implementa ControlValueAccessor.
Selector: lib-input-time-filter
Inputs:
filters: FilterItem[](requerido) – Filtros de hora disponibles.clearTrigger: number = 0– Al incrementar este valor desde el padre se limpian todos los filtros.initialValues: Record<string, string> = {}– Horas a restaurar al inicializar en formatoHH:mm(24 h), donde la clave es elFilterItem.value. Al limpiar un chip individualmente el valor inicial queda descartado y no se restaura automáticamente.
Outputs:
filterSelected: EventEmitter<{ filter: string; value: string }>– Emite el filtro activo y el valor en formatoHH:mm(24 h).valueChange: EventEmitter<string | null>– Emite la hora seleccionada onullal limpiar.enterPressed: EventEmitter<void>– Emite al presionar Enter con el dropdown abierto.
Ejemplo de uso:
<lib-input-time-filter
[filters]="timeFilters"
[clearTrigger]="clearVersion"
[initialValues]="{ startTime: '08:00', endTime: '17:00' }"
(filterSelected)="onTimeFilter($event)"
(valueChange)="onTimeChange($event)"
></lib-input-time-filter>timeFilters: FilterItem[] = [
{ label: 'Hora inicio', value: 'startTime', type: 'time', placeholder: 'Hora inicio' },
{ label: 'Hora fin', value: 'endTime', type: 'time', placeholder: 'Hora fin' },
];39. lib-pdf-viewer (PdfViewer) y PdfViewerService
Qué hacen Sistema de visualización de documentos (PDF e imágenes) en modal:
lib-pdf-viewer: componente de modal que renderiza el documento activo según el servicio. Soporta múltiples URLs con navegación anterior/siguiente.PdfViewerService: servicio singleton para abrir el visor y establecer el documento desde cualquier parte de la aplicación.
Selector: lib-pdf-viewer
Declarar <lib-pdf-viewer></lib-pdf-viewer> una sola vez (por ejemplo en AppComponent).
PdfViewerService — API:
| Método | Descripción |
|---|---|
| open(doc: FileViewerDoc) | Abre el visor con el documento indicado (recomendado) |
| openModal() | Muestra el modal sin cambiar el documento |
| closeModal() | Cierra el modal |
| setSelectedPdf(doc \| null) | Establece el documento sin abrir el modal |
FileViewerDoc:
export interface FileViewerDoc {
name: string;
urls: string[]; // Una o varias URLs
type?: 'pdf' | 'image'; // Si no se indica, se detecta por extensión
}Ejemplo de uso:
import { PdfViewerService } from 'sapenlinea-components';
constructor(private pdfViewer: PdfViewerService) {}
verDocumento(url: string) {
this.pdfViewer.open({ name: 'Comparendo', urls: [url] });
}
verImagenes(urls: string[]) {
this.pdfViewer.open({ name: 'Evidencias', urls, type: 'image' });
}40. lib-document-upload (DocumentUpload)
Qué hace Zona de arrastrar y soltar archivos (Drag & Drop) con validación de tipo, tamaño máximo y lista de archivos adjuntados. Permite selección manual y eliminación individual.
Selector: lib-document-upload
Inputs:
label: string = 'Arrastra los archivos aquí o haz clic para seleccionar'helperText: string = 'Máximo 25 MB por archivo'accept: string = '.pdf,application/pdf'– Valor para el atributoacceptdel input de archivo (ej."image/*",".pdf,.png").maxSizeMB: number = 25– Tamaño máximo permitido por archivo en MB.multiple: boolean = true– Permite adjuntar varios archivos.
Outputs:
filesChanged: OutputEmitterRef<File[]>– Emite el arreglo completo de archivos válidos cada vez que cambia.
Ejemplo de uso:
<lib-document-upload
label="Arrastra los soportes aquí"
helperText="Solo PDF, máximo 10 MB"
accept=".pdf"
[maxSizeMB]="10"
(filesChanged)="onFilesChanged($event)"
/>onFilesChanged(files: File[]) {
this.archivos = files;
}41. lib-document-item (DocumentItem)
Qué hace Elemento de lista que representa un documento adjunto (PDF, imagen o video) con fecha, nombre, folio opcional y botón de visualización.
Selector: lib-document-item
Input:
data: DocumentInfo– Información del documento:
export interface DocumentInfo {
folio?: string;
name: string;
creation_date: string; // ISO 8601
type: 'document' | 'image' | 'video';
actionKey: string;
url?: string; // URL para previsualización o descarga
}Output:
view: OutputEmitterRef<DocumentInfo>– Emite el documento al pulsar la acción de ver.
Ejemplo de uso:
<lib-document-item
[data]="doc"
(view)="onViewDocument($event)"
/>onViewDocument(doc: DocumentInfo) {
this.pdfViewer.open({ name: doc.name, urls: [doc.url!] });
}42. lib-card-history (CardHistory)
Qué hace
Tarjeta de línea de tiempo / historial con información de cambio de estado, fecha/hora y descripción expandible. Muestra previousStatus → currentStatus con badges de color semántico.
Selector: lib-card-history
Inputs:
DataCard: DataCard– Datos de la entrada de historial:
export interface DataCard {
title: string;
time: string;
date: string; // ISO 8601
previousStatus?: any;
currentStatus?: any;
type: string;
responsible?: string;
description?: string;
actionType?: 'create' | 'update' | 'delete';
}withResponsible: boolean = true– Muestra u oculta el nombre del responsable.
Badges de estado disponibles:
| Valor normalizado | Badge |
|---|---|
| activo | Verde (badge-active) |
| inactivo | Gris (badge-inactive) |
| eliminado | Rojo oscuro (badge-retired) |
| obsoleto | Amarillo (badge-obsolete) |
| operativo |
