npm.io
0.1.6 • Published 3d ago

@telcomdev/ui

Licence
MIT
Version
0.1.6
Deps
1
Size
688 kB
Vulns
0
Weekly
1.5K

Telcomdev UI

Input outline

<td-input
  label="Nombre completo"
  icon="person"
  [clearable]="true"
  [(ngModel)]="nombre"
/>

TdInput es compatible con ngModel y formularios reactivos. La etiqueta y el borde outline permanecen visibles en todos los estados.

Selects

<td-select
  label="Estado"
  [options]="estados"
  [(ngModel)]="estado"
/>

<td-autocomplete-select
  label="Ciudad"
  [options]="ciudades"
  [(ngModel)]="ciudad"
/>

Ambos controles usan TdSelectOption<T>, son compatibles con formularios Angular y muestran su panel mediante Angular CDK Overlay.

El comportamiento predeterminado durante el scroll es reposition, por lo que el panel permanece unido al control. Puede cambiarse con scrollBehavior="close".

Tabs

<td-tabs [(selectedIndex)]="indice">
  <td-tab label="General" icon="settings">
    <app-general />
  </td-tab>
  <td-tab label="Usuarios" icon="people" [badge]="12">
    <app-usuarios />
  </td-tab>
</td-tabs>

Incluye las variantes linea, pastilla y contenida, además de navegación con flechas, Home y End.

Para usar componentes basados en Overlay, como TdDialog, agrega el estilo estructural del CDK en angular.json:

"styles": [
  "node_modules/@angular/cdk/overlay-prebuilt.css",
  "src/styles.scss"
]

Los diálogos pueden abrir cualquier componente standalone. El contenido decide libremente sus acciones:

<td-dialog-actions>
  <button type="button" [tdDialogClose]="false">Cancelar</button>
  <button type="button" (click)="guardarBorrador()">Borrador</button>
  <button type="submit" tdDialogPrimary>Guardar</button>
</td-dialog-actions>

Iconos

import { TdIcon, TdIconoRegistry } from '@telcomdev/ui';

@Component({
  imports: [TdIcon],
})
export class App {
  constructor(registry: TdIconoRegistry) {
    registry.registrar({
      estrella: 'M12 2 15 9 22 9 17 14 19 22 12 17 5 22 7 14 2 9 9 9Z',
    });
  }
}
<td-icon nombre="security" tamano="24px" />
<td-icon nombre="estrella" titulo="Favorito" />

El icono hereda el color CSS mediante currentColor. Si el nombre no existe, se muestra folder.

Data table

TdDataTable usa @angular/cdk/scrolling para virtualizar las filas. La aplicación consumidora mantiene la responsabilidad de consultar, paginar y modificar datos.

import {
  TdDataTable,
  TdDataTableAccion,
  TdDataTableAccionEvento,
  TdDataTableBoton,
  TdDataTableColumna,
} from '@telcomdev/ui';

@Component({
  imports: [TdDataTable],
})
export class Establecimientos {
  columnas: TdDataTableColumna<Establecimiento>[] = [
    { clave: 'nombre', encabezado: 'Nombre', ancho: '1fr' },
    { clave: 'codigoSunat', encabezado: 'Código Sunat', ancho: '8rem' },
    { clave: 'direccion', encabezado: 'Dirección', ancho: '1.5fr' },
    { clave: 'esPrincipal', encabezado: 'Principal', tipo: 'booleano' },
    { clave: 'activo', encabezado: 'Estado', tipo: 'estado' },
  ];

  acciones: TdDataTableAccion<Establecimiento>[] = [
    { id: 'editar', etiqueta: 'Editar', icono: 'edit' },
    { id: 'eliminar', etiqueta: 'Eliminar', icono: 'delete', tono: 'peligro' },
  ];

  botones: TdDataTableBoton[] = [
    { id: 'agregar', etiqueta: 'Agregar establecimiento', icono: 'add', tono: 'primario' },
    { id: 'pdf', etiqueta: 'Exportar PDF', icono: 'pdf', tono: 'peligro' },
    { id: 'excel', etiqueta: 'Exportar Excel', icono: 'description', tono: 'exito' },
  ];

  ejecutar(evento: TdDataTableAccionEvento<Establecimiento>) {
    // evento.accion.id, evento.fila y evento.indice
  }

  ejecutarBoton(boton: TdDataTableBoton) {
    if (boton.id === 'agregar') this.abrirModal(0);
    if (boton.id === 'pdf') this.exportarPdf();
  }
}
<td-data-table
  campoId="establecimientoId"
  [datos]="lista()"
  [columnas]="columnas"
  [acciones]="acciones"
  [botones]="botones"
  [total]="totalEstablecimientos()"
  [cargando]="cargando()"
  [filasAntesDeCargar]="5"
  [puedeCargarMas]="paginaActual() < totalPaginas()"
  (busquedaChange)="buscar($event)"
  (accion)="ejecutar($event)"
  (botonClick)="ejecutarBoton($event)"
  (cargarMas)="paginaSiguiente()"
>
  <select tdDataTableFilters [(ngModel)]="estado">
    <option value="">Todos los estados</option>
    <option value="activo">Activos</option>
    <option value="inactivo">Inactivos</option>
  </select>

  <input tdDataTableFilters type="date" [(ngModel)]="fecha" />

  <button tdDataTableActions type="button">Acción personalizada</button>
</td-data-table>

Incluye búsqueda con debounce, scroll virtual, carga incremental, chips de estado, acciones condicionales, tema oscuro, estado vacío y skeleton de carga. Los elementos marcados con tdDataTableFilters se colocan junto al buscador. Los elementos tdDataTableActions aparecen junto al contador y al botón Agregar.

Header

import { TdHeader } from '@telcomdev/ui';

@Component({
  imports: [TdHeader],
})
export class App {
  sidebarAbierto = true;
}
<td-header
  [menuAbierto]="sidebarAbierto"
  [oscuro]="oscuro"
  usuario="Andrea Torres"
  cargo="Administradora"
  [notificaciones]="notificaciones"
  (menuToggle)="sidebarAbierto = !sidebarAbierto"
  (notificacionSeleccionada)="abrirNotificacion($event)"
  (verTodasNotificaciones)="verNotificaciones()"
  (accionPerfil)="ejecutarAccionPerfil($event)"
>
  <button tdHeaderActions type="button">Acción adicional</button>
</td-header>

El header incluye dropdowns funcionales de notificaciones y perfil, avatar por iniciales, modo responsive y tema oscuro. Las acciones tdHeaderActions aparecen a la derecha.

import { TdFooter } from '@telcomdev/ui';

@Component({
  imports: [TdFooter],
})
export class App {}
<td-footer
  empresa="TelcomDev"
  texto="Todos los derechos reservados."
  version="1.0.0"
  [oscuro]="oscuro"
>
  <a tdFooterLinks href="/privacidad">Privacidad</a>
  <span tdFooterActions>Angular 21</span>
</td-footer>

El footer calcula el año actual por defecto y admite el modo compacto.

Sidebar

TdSidebar es standalone y no depende de Angular Material ni Tailwind.

import { MenuSidebar, TdSidebar } from '@telcomdev/ui';

@Component({
  imports: [TdSidebar],
})
export class App {
  abierto = true;
  oscuro = false;

  menu: MenuSidebar[] = [
    {
      nombre: 'Inicio',
      ruta: '/',
      icono: 'home',
      orden: 1,
      subMenus: [],
    },
  ];
}
<td-sidebar
  titulo="Gestión Corporativa"
  subtitulo="Central Platform"
  [menu]="menu"
  [(abierto)]="abierto"
  modoCerrado="oculto"
  [oscuro]="oscuro"
  [cargando]="false"
  error=""
  (seleccionar)="onSeleccionar($event)"
/>

El botón para abrir o cerrar debe vivir en el header de la aplicación:

<button type="button" (click)="abierto = !abierto">
  <td-icon [nombre]="abierto ? 'close' : 'menu'" />
</button>

modoCerrado acepta compacto para conservar una barra de iconos u oculto para cerrar completamente el sidebar.

La aplicación consumidora obtiene el menú desde su propia API. El componente ordena cada nivel por orden, usa folder cuando no recibe icono y renderiza los submenús recursivamente.

Iconos incluidos: home, dashboard, folder, settings, people, person, security, inventory, shopping_cart, receipt, assessment, description y business.

Signal Forms

TdInput, TdSelect, TdAutocompleteSelect y TdDatePicker implementan FormValueControl y conservan soporte para ngModel y Reactive Forms.

Cuando existe [formField], el field pasa a ser la fuente principal del valor y del estado. Los controles reciben automáticamente errors, invalid, required, readonly, disabled, min, max, minLength, maxLength y pattern cuando corresponda. Las propiedades tradicionales (value, valueChange, error, disabled, etc.) continúan disponibles para no exigir una migración completa.

TdInput acepta fields string, number y sus variantes anulables. Un valor null se presenta como un input vacío. Con type="number" y Signal Forms, el control entrega number | null, evitando números almacenados como texto.

La política de vaciado se resuelve automáticamente y puede fijarse cuando sea necesario:

<td-input type="number" [formField]="fields.cantidad" />
<td-input [formField]="fields.correo" emptyValue="null" />

emptyValue acepta auto (predeterminado), null y empty-string. En modo tradicional se conserva el comportamiento histórico: type="number" continúa emitiendo texto mediante CVA para no romper formularios existentes.

Cuando el control recibe maxlength —de forma directa o mediante la metadata maxLength de Signal Forms— muestra automáticamente un contador actual/max. Puede ocultarse sin quitar la validación:

<td-input [maxlength]="100" [(ngModel)]="nombre" />
<td-input [formField]="fields.descripcion" [showCounter]="false" />

minlength, min, max y pattern no activan el contador.

Checkbox

TdCheckbox implementa el contrato FormCheckboxControl de Angular y mantiene compatibilidad con ControlValueAccessor. Su valor siempre es booleano:

<td-checkbox
  label="Establecimiento principal"
  description="Se utilizará como establecimiento predeterminado."
  [formField]="establecimientoForm.esPrincipal"
/>

También funciona con formularios tradicionales:

<td-checkbox label="Activo" [(ngModel)]="activo" />
<td-checkbox label="Principal" [formControl]="controlPrincipal" />

Con Signal Forms no se necesita una variable intermedia ni eventos manuales. El modelo debe inicializar el campo como true o false, nunca como null.

import { signal } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';

readonly model = signal({
  nombre: '',
  periodo: null as TdDatePickerValue,
});
readonly fields = form(this.model);

Agrega FormField a los imports del componente. La librería no registra un segundo selector [formField], evitando conflictos con la directiva oficial y con la transformación especial del compilador de Angular.

<td-input [formField]="fields.nombre" />
<td-date-picker mode="rango" [formField]="fields.periodo" />

El uso clásico sigue siendo válido:

<td-input [(ngModel)]="nombre" />
<td-select [formControl]="form.controls.estado" [options]="estados" />

Date Picker

<td-date-picker
  label="Fecha de emisión"
  min="2026-01-01"
  max="2027-12-31"
  [(ngModel)]="fecha"
/>

<td-date-picker
  label="Periodo"
  mode="rango"
  [(ngModel)]="rango"
/>

Las fechas se almacenan como YYYY-MM-DD. En modo rango el valor tiene la forma { inicio: string, fin: string | null }.

Build

ng build telcomdev-ui

Keywords