ten-minds-ui-kit
v0.0.1
Published
Librería de componentes UI para proyectos Angular de Ten Minds
Downloads
106
Maintainers
Readme
@10mindssoftware/tm-ui-kit
Librería de componentes UI para Angular, construida con Tailwind CSS v4 y Lucide Icons.
Instalación
npm install @10mindssoftware/tm-ui-kitConfiguración inicial
1.- Instalar la sgte libreria lucide-angular
npm i lucide-angular
1. Agregar los estilos de la librería
Importá los estilos de la librería en el styles.css de tu proyecto:
@import '@10mindssoftware/tm-ui-kit/styles/styles.css';Customización de Estilos
La librería @10mindssoftware/tm-ui-kit ofrece tres niveles de personalización según el alcance que necesites.
Nivel 1 — Global: sobreescribir tokens CSS
Afecta todos los componentes que usen ese token en toda la app.
Agrega esto en el styles.css de tu proyecto:
:root {
--tm-gray-900: #your-brand-color;
}O directamente el token semántico:
:root {
--color-tm-brand: #your-brand-color;
--color-tm-primary: #your-text-color;
}Tokens disponibles
| Token | Descripción | Default (light) |
| -------------------------------- | --------------------------- | --------------- |
| --color-tm-brand | Color principal de la marca | #141414 |
| --color-tm-primary | Texto principal | #141414 |
| --color-tm-secondary | Texto secundario | #737373 |
| --color-tm-tertiary | Texto terciario | #A0A0A0 |
| --color-tm-inverse | Texto sobre fondos oscuros | #FFFFFF |
| --color-tm-canvas | Fondo general de la app | #FAFAFA |
| --color-tm-surface | Fondo de tarjetas y paneles | #FFFFFF |
| --color-tm-subtle | Fondo sutil | #F0F0F0 |
| --color-tm-muted | Fondo atenuado | #E4E4E4 |
| --color-tm-brand-hover-default | Hover del botón brand | #363636 |
| --color-tm-brand-hover-subtle | Hover sutil del botón brand | #F0F0F0 |
| --color-tm-default | Borde por defecto | #E4E4E4 |
| --color-tm-strong | Borde fuerte | #C8C8C8 |
Nivel 2 — Scoped: cambiar un componente en una sección
Afecta solo los componentes dentro de un contenedor específico. Se usa CSS puro — no Tailwind.
/* styles.css del proyecto consumidor */
.mi-seccion tm-btn-primary button {
background-color: #your-color !important;
font-size: 1.25rem !important;
border-radius: 999px !important;
}<div class="mi-seccion">
<tm-btn-primary>Solo este botón cambia</tm-btn-primary>
</div>
<tm-btn-primary>Este queda igual</tm-btn-primary>⚠️ CSS puro únicamente. Tailwind no funciona aquí porque se compila en build time y las clases de la librería no están disponibles en el proyecto consumidor.
Nivel 3 — Puntual: un solo componente con customClass
Afecta únicamente el componente donde lo aplicas. Aquí sí puedes usar clases de Tailwind porque se compilan en el proyecto consumidor.
<!-- Tailwind funciona aquí -->
<tm-btn-primary customClass="!bg-orange-500 !text-black"> Botón personalizado </tm-btn-primary>
<!-- CSS también funciona con clase propia -->
<tm-btn-primary customClass="mi-boton-especial"> Botón personalizado </tm-btn-primary>/* styles.css */
.mi-boton-especial {
background-color: orange !important;
font-size: 1.25rem !important;
}ℹ️ El modificador
!de Tailwind es necesario para sobreescribir estilos ya definidos por el componente.
Resumen
| Método | Alcance | Tailwind | Cuándo usarlo |
| -------------------------- | ------------- | -------- | ----------------------------------------- |
| Token CSS en :root | Toda la app | ✗ | Cambiar la paleta de colores del proyecto |
| CSS scoped con clase padre | Una sección | ✗ | Variante especial en una página o sección |
| customClass input | Un componente | ✓ | Ajuste puntual en un solo componente |
2. Importar los componentes
Los componentes son standalone — solo importa los que necesites directamente en tu componente o módulo:
import { BtnPrimary, Chip, CheckboxButton } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [BtnPrimary, Chip, CheckboxButton],
})3. Usar íconos
Los íconos se importan directamente desde lucide-angular como objetos. No se requiere ninguna configuración global en app.config.ts.
import { Download, Trash2, Check } from 'lucide-angular';
export class MiComponente {
download = Download;
trash = Trash2;
check = Check;
}<tm-btn-primary [iconLeft]="download">Exportar</tm-btn-primary>
<tm-chip [iconLeft]="check">Activo</tm-chip>Componentes disponibles
| Componente | Selector | Descripción |
| ---------------- | ------------------ | --------------------------------------- |
| BtnPrimary | tm-btn-primary | Botón principal con múltiples variantes |
| Chip | tm-chip | Etiqueta de estado o categoría |
| CheckboxButton | tm-checkbox | Checkbox personalizado |
| Toggle | tm-toggle | Switch on/off |
| RadioButton | tm-radio-button | Botón de selección única |
| InputTen | tm-input | Campo de texto |
| Avatar | tm-avatar | Avatar con iniciales y estado |
| SnackbarHost | tm-snackbar-host | Contenedor de notificaciones |
Dark Mode
La librería soporta dark mode mediante la clase .dark en el elemento raíz. Agrégala o quítala desde tu componente:
onThemeChange(mode: 'light' | 'dark') {
if (mode === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}Componentes
Avatar
Componente de avatar de la librería ten-minds-beta. Soporta imagen, iniciales como fallback, múltiples tamaños e indicador de estado.
Importación
import { Avatar } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [Avatar],
})API
Inputs
| Prop | Tipo | Default | Descripción |
| ---------- | ---------------------- | ---------- | ---------------------------------------------------------- |
| src | string | '' | URL de la imagen. Si está vacío, se muestran las iniciales |
| initials | string | '' | Texto de iniciales, se muestra cuando no hay imagen |
| alt | string | 'Avatar' | Texto alternativo para accesibilidad |
| size | AvatarSize | 'md' | Tamaño del avatar |
| status | AvatarStatus \| null | null | Indicador de estado visible sobre el avatar |
Tipos
type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
type AvatarStatus = 'online' | 'busy' | 'disconnect';Tamaños de referencia
| Valor | Tamaño |
| ----- | ------ |
| xs | 28px |
| sm | 40px |
| md | 48px |
| lg | 60px |
| xl | 80px |
Ejemplos
Con imagen
<tm-avatar src="https://ejemplo.com/foto.jpg" />
<tm-avatar src="https://ejemplo.com/foto.jpg" alt="Foto de Juan" />Con iniciales (fallback sin imagen)
<tm-avatar initials="JP" /> <tm-avatar initials="AB" />Si
srcestá vacío einitialstiene valor, se muestran las iniciales sobre el fondo de marca.
Tamaños
<tm-avatar src="..." size="xs" />
<tm-avatar src="..." size="sm" />
<tm-avatar src="..." size="md" />
<tm-avatar src="..." size="lg" />
<tm-avatar src="..." size="xl" />Indicador de estado
<tm-avatar src="..." status="online" />
<tm-avatar src="..." status="busy" />
<tm-avatar src="..." status="disconnect" />Sin estado (por defecto)
<tm-avatar src="..." />
<!-- status es null por defecto, no se muestra el indicador -->Combinaciones comunes
<!-- Avatar con iniciales y estado online -->
<tm-avatar initials="MG" status="online" />
<!-- Avatar grande con imagen y estado ocupado -->
<tm-avatar src="https://ejemplo.com/foto.jpg" alt="María García" size="lg" status="busy" />
<!-- Avatar pequeño desconectado -->
<tm-avatar src="https://ejemplo.com/foto.jpg" size="sm" status="disconnect" />
<!-- Avatar extra grande solo con iniciales -->
<tm-avatar initials="TM" size="xl" />Notas
- Si
srctiene valor, se muestra la imagen. Si no, se recurre a lasinitials. Si tampoco hay iniciales, el avatar queda vacío con el fondo de marca. - El indicador de
statusescala automáticamente según elsizedel avatar y se posiciona en la esquina inferior derecha. - El indicador incluye
role="status"yaria-labelcon el valor del estado para accesibilidad.
Componente BtnIcon
Componente de botón de ícono de la librería @10mindssoftware/tm-ui-kit. Versión cuadrada del botón principal, diseñado para acciones que se representan únicamente con un ícono. Soporta los mismos colores, variantes, tamaños y bordes redondeados que BtnPrimary.
Importación
import { BtnIcon } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [BtnIcon],
})API
Inputs
| Prop | Tipo | Default | Descripción |
| ----------- | ---------------- | ----------- | ------------------------------------------------------------------------ |
| icon | LucideIconData | — | Requerido. Ícono a mostrar dentro del botón |
| color | ButtonColor | 'primary' | Color del botón |
| size | ButtonSize | 'md' | Tamaño del botón |
| type | ButtonType | 'filled' | Variante visual |
| rounded | ButtonRounded | 'default' | Forma del borde |
| active | boolean | false | Aplica estado activo (brightness) |
| disabled | boolean | false | Desactiva el botón |
| ariaLabel | string | '' | Texto accesible del botón. Recomendado siempre que no haya texto visible |
Outputs
| Evento | Tipo | Descripción |
| --------- | ------------ | --------------------------------------------------- |
| clicked | MouseEvent | Se emite al hacer click (no emite si está disabled) |
Tipos
type ButtonColor = 'primary' | 'danger' | 'success' | 'warning';
type ButtonSize = 'lg' | 'md' | 'sm';
type ButtonType = 'filled' | 'outlined' | 'ghost';
type ButtonRounded = 'default' | 'full';Tamaños de referencia
| Valor | Tamaño | Ícono |
| ----- | ------ | ----- |
| lg | 44px | 20px |
| md | 40px | 18px |
| sm | 36px | 16px |
Ejemplos
Uso básico
El prop icon es requerido. Siempre importa el ícono desde lucide-angular como objeto.
import { Trash2, Edit, Plus } from 'lucide-angular';
export class App {
trash = Trash2;
edit = Edit;
plus = Plus;
}<tm-icon-button [icon]="edit" ariaLabel="Editar" />Colores
<tm-icon-button [icon]="edit" color="primary" ariaLabel="Editar" />
<tm-icon-button [icon]="trash" color="danger" ariaLabel="Eliminar" />
<tm-icon-button [icon]="plus" color="success" ariaLabel="Agregar" />
<tm-icon-button [icon]="edit" color="warning" ariaLabel="Advertencia" />Variantes
<tm-icon-button [icon]="edit" type="filled" ariaLabel="Editar" />
<tm-icon-button [icon]="edit" type="outlined" ariaLabel="Editar" />
<tm-icon-button [icon]="edit" type="ghost" ariaLabel="Editar" />Tamaños
<tm-icon-button [icon]="edit" size="lg" ariaLabel="Editar" />
<tm-icon-button [icon]="edit" size="md" ariaLabel="Editar" />
<tm-icon-button [icon]="edit" size="sm" ariaLabel="Editar" />Bordes redondeados
<tm-icon-button [icon]="plus" rounded="default" ariaLabel="Agregar" />
<tm-icon-button [icon]="plus" rounded="full" ariaLabel="Agregar" />Disabled
<tm-icon-button [icon]="trash" [disabled]="true" ariaLabel="Eliminar" />
<tm-icon-button
[icon]="trash"
color="danger"
type="outlined"
[disabled]="true"
ariaLabel="Eliminar"
/>Manejo de eventos
<tm-icon-button [icon]="trash" color="danger" ariaLabel="Eliminar" (clicked)="onEliminar($event)" />onEliminar(event: MouseEvent) {
console.log('Eliminando...', event);
}Combinaciones comunes
<!-- Botón de acción flotante (FAB) -->
<tm-icon-button
[icon]="plus"
color="primary"
size="lg"
rounded="full"
ariaLabel="Crear nuevo"
(clicked)="onCreate($event)"
/>
<!-- Botón destructivo ghost -->
<tm-icon-button
[icon]="trash"
color="danger"
type="ghost"
size="sm"
ariaLabel="Eliminar fila"
(clicked)="onDelete($event)"
/>
<!-- Botón de edición outlined -->
<tm-icon-button
[icon]="edit"
color="primary"
type="outlined"
ariaLabel="Editar registro"
(clicked)="onEdit($event)"
/>Notas
- El prop
icones requerido. El componente no renderiza nada sin él. - El evento
clickedno se emite cuandodisabledestrue, no es necesario manejarlo manualmente. - Se recomienda proporcionar siempre un
ariaLabeldescriptivo ya que el botón no contiene texto visible, lo cual es importante para accesibilidad. - Los íconos deben importarse desde
lucide-angularcomo objetos y asignarse a propiedades del componente. Pasar strings directamente no funciona. - El tamaño del ícono se ajusta automáticamente según el
sizedel botón.
tm-button — Componente de Botón
Componente de botón de la librería @10mindssoftware/tm-ui-kit. Soporta múltiples colores, variantes, tamaños, bordes redondeados e íconos.
Instalación
Importa el componente en tu componente standalone:
import { BtnPrimary } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [BtnPrimary],
})API
Inputs
| Prop | Tipo | Default | Descripción |
| ------------- | ---------------- | ----------- | ---------------------- |
| color | ButtonColor | 'primary' | Color del botón |
| size | ButtonSize | 'md' | Tamaño del botón |
| type | ButtonType | 'filled' | Variante visual |
| rounded | ButtonRounded | 'default' | Forma del borde |
| disabled | boolean | false | Desactiva el botón |
| active | boolean | false | Aplica estado activo |
| iconLeft | LucideIconData | — | Ícono a la izquierda |
| iconRight | LucideIconData | — | Ícono a la derecha |
| customClass | string | '' | Clases CSS adicionales |
Outputs
| Evento | Tipo | Descripción |
| ----------- | ------------ | --------------------------------------------------- |
| (clicked) | MouseEvent | Se emite al hacer click (no emite si está disabled) |
Tipos
type ButtonColor = 'primary' | 'danger' | 'success' | 'warning';
type ButtonSize = 'lg' | 'md' | 'sm';
type ButtonType = 'filled' | 'outlined' | 'ghost';
type ButtonRounded = 'default' | 'full';Ejemplos
Uso básico
<tm-button>Guardar</tm-button>Colores
<tm-button color="primary">Primary</tm-button>
<tm-button color="danger">Danger</tm-button>
<tm-button color="success">Success</tm-button>
<tm-button color="warning">Warning</tm-button>Variantes
<tm-button type="filled">Filled</tm-button>
<tm-button type="outlined">Outlined</tm-button>
<tm-button type="ghost">Ghost</tm-button>Tamaños
<tm-button size="lg">Large</tm-button>
<tm-button size="md">Medium</tm-button>
<tm-button size="sm">Small</tm-button>Bordes redondeados
<tm-button rounded="default">Default</tm-button> <tm-button rounded="full">Pill</tm-button>Disabled
<tm-button [disabled]="true">No disponible</tm-button>
<tm-button color="danger" type="outlined" [disabled]="true">Eliminar</tm-button>Con íconos
Los íconos se importan desde lucide-angular como objetos y se asignan a propiedades del componente:
import { Download, Trash2, Check } from 'lucide-angular';
export class MiComponente {
download = Download;
trash = Trash2;
check = Check;
}<!-- Ícono izquierdo -->
<tm-button [iconLeft]="download">Exportar</tm-button>
<!-- Ícono derecho -->
<tm-button color="success" [iconRight]="check">Confirmar</tm-button>
<!-- Ambos íconos -->
<tm-button color="danger" type="outlined" [iconLeft]="trash" [iconRight]="check">
Eliminar
</tm-button>Manejo de eventos
<tm-button color="success" (clicked)="onGuardar($event)">Guardar</tm-button>onGuardar(event: MouseEvent) {
console.log('Guardando...', event);
}Combinaciones comunes
<!-- Botón de acción principal -->
<tm-button color="primary" size="md" [iconLeft]="download">Exportar</tm-button>
<!-- Botón destructivo -->
<tm-button color="danger" type="outlined" size="md" [iconLeft]="trash">Eliminar</tm-button>
<!-- Botón pill -->
<tm-button color="primary" rounded="full" [iconLeft]="check">Confirmar</tm-button>
<!-- Botón pequeño ghost -->
<tm-button color="primary" type="ghost" size="sm">Ver más</tm-button>Notas
- El evento
(clicked)no se emite cuandodisabledestrue. - Los íconos deben importarse desde
lucide-angularcomo objetos. Pasar strings no funciona. - Usa
customClasspara agregar clases adicionales sin sobreescribir las base.
Componente Btn Theme
Componente de botón para alternar entre modo claro y oscuro. Cambia el icono según el modo actual y emite el nuevo modo al padre.
Instalación
import { BtnTheme } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [BtnTheme],
})Uso básico
<lib-btn-theme [mode]="currentTheme" (modeChange)="onThemeChange($event)" />import { ThemeMode } from '@10mindssoftware/tm-ui-kit';
currentTheme: ThemeMode = 'light';
onThemeChange(mode: ThemeMode) {
this.currentTheme = mode;
if (mode === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}Inputs y Outputs
| Input | Tipo | Default | Descripción |
| ---------- | ----------- | --------- | ------------------------ |
| mode | ThemeMode | 'light' | Modo actual del tema |
| disabled | boolean | false | Desactiva la interacción |
| Output | Tipo | Descripción |
| ------------ | ----------- | ---------------------------------- |
| modeChange | ThemeMode | Emite el nuevo modo al hacer click |
Tipos
type ThemeMode = 'light' | 'dark';Estados
Light (modo claro)
<lib-btn-theme mode="light" (modeChange)="onThemeChange($event)" />Dark (modo oscuro)
<lib-btn-theme mode="dark" (modeChange)="onThemeChange($event)" />Disabled
<lib-btn-theme [disabled]="true" />Casos de uso reales
Toggle global en el navbar
<nav class="flex items-center justify-between px-6 py-4">
<span>Mi App</span>
<lib-btn-theme [mode]="currentTheme" (modeChange)="onThemeChange($event)" />
</nav>currentTheme: ThemeMode = 'light';
onThemeChange(mode: ThemeMode) {
this.currentTheme = mode;
if (mode === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}Persistir en localStorage
currentTheme: ThemeMode = 'light';
ngOnInit() {
const saved = localStorage.getItem('theme') as ThemeMode;
if (saved) {
this.currentTheme = saved;
document.documentElement.classList.toggle('dark', saved === 'dark');
}
}
onThemeChange(mode: ThemeMode) {
this.currentTheme = mode;
localStorage.setItem('theme', mode);
document.documentElement.classList.toggle('dark', mode === 'dark');
}Componente CardButton
Componente de botón de carrito de compras de la librería @10mindssoftware/tm-ui-kit. Muestra un ícono de carrito con un badge numérico opcional según la cantidad de ítems.
Importación
import { CardButton } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [CardButton],
})API
Inputs
| Prop | Tipo | Default | Descripción |
| ----------- | --------- | ---------------------- | -------------------------------------------------------------------------------------- |
| count | number | 0 | Cantidad de ítems en el carrito. Si es 0, no se muestra el badge |
| disabled | boolean | false | Desactiva el botón |
| ariaLabel | string | 'Carrito de compras' | Texto accesible del botón. Se complementa automáticamente con la cantidad si hay ítems |
Outputs
| Evento | Tipo | Descripción |
| --------- | ------ | --------------------------------------------------- |
| clicked | void | Se emite al hacer click (no emite si está disabled) |
Ejemplos
Uso básico
<tm-card-button />Con ítems en el carrito
<tm-card-button [count]="3" /> <tm-card-button [count]="9" />Cuando
countsupera 9, el badge muestra9+en lugar del número exacto.
<tm-card-button [count]="15" />
<!-- Badge muestra: 9+ -->Deshabilitado
<tm-card-button [disabled]="true" /> <tm-card-button [count]="4" [disabled]="true" />Manejo de eventos
<tm-card-button [count]="itemCount" (clicked)="onCartClick()" />itemCount = 2;
onCartClick() {
console.log('Abriendo carrito...');
}Con aria-label personalizado
<tm-card-button ariaLabel="Ver mi pedido" [count]="2" />
<!-- aria-label resultante: "Ver mi pedido, 2 items" -->Combinaciones comunes
<!-- Carrito vacío -->
<tm-card-button (clicked)="abrirCarrito()" />
<!-- Carrito con items -->
<tm-card-button [count]="totalItems" (clicked)="abrirCarrito()" />
<!-- Carrito deshabilitado durante carga -->
<tm-card-button [count]="totalItems" [disabled]="cargando" (clicked)="abrirCarrito()" />Notas
- El badge solo es visible cuando
countes mayor a0. - Cuando
countsupera 9, el badge muestra9+para evitar desbordamiento visual. - El evento
clickedno se emite cuandodisabledestrue, no es necesario manejarlo manualmente. - El
aria-labelse construye dinámicamente: si hay ítems, añade la cantidad al texto base (ej."Carrito de compras, 3 items"). El badge tienearia-hidden="true"para evitar duplicados en lectores de pantalla. - El ícono del carrito es fijo (
ShoppingCartdelucide-angular) y no es configurable.
Componente Checkbox
Componente de selección para formularios. Permite marcar o desmarcar una opción individual. Semánticamente correcto y accesible.
Instalación
import { CheckboxButton } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [CheckboxButton],
})Uso básico
<tm-checkbox-button [checked]="isChecked" (changed)="isChecked = $event" />API
Inputs
| Input | Tipo | Default | Descripción |
| ---------- | --------- | ------------- | ----------------------------------------- |
| checked | boolean | false | Estado actual del checkbox |
| disabled | boolean | false | Desactiva la interacción |
| label | string | — | Texto descriptivo al lado del checkbox |
| id | string | auto-generado | ID del input para asociar labels externos |
Outputs
| Output | Tipo | Descripción |
| ----------- | --------- | ------------------------------------ |
| (changed) | boolean | Emite el nuevo estado al hacer click |
Estados
<!-- Desmarcado -->
<tm-checkbox-button [checked]="false" (changed)="isChecked = $event" />
<!-- Marcado -->
<tm-checkbox-button [checked]="true" (changed)="isChecked = $event" />
<!-- Disabled desmarcado -->
<tm-checkbox-button [checked]="false" [disabled]="true" />
<!-- Disabled marcado -->
<tm-checkbox-button [checked]="true" [disabled]="true" />Con label
<tm-checkbox-button
[checked]="isChecked"
label="Aceptar términos y condiciones"
(changed)="isChecked = $event"
/>Manejo del estado
El checkbox no guarda estado internamente. El padre es responsable de actualizar el valor:
isChecked = false;
onChanged(value: boolean) {
this.isChecked = value;
}<tm-checkbox-button [checked]="isChecked" (changed)="onChanged($event)" />Casos de uso reales
Aceptar términos
<tm-checkbox-button
[checked]="aceptaTerminos"
label="Acepto los términos y condiciones"
(changed)="aceptaTerminos = $event"
/>
<tm-button [disabled]="!aceptaTerminos" (clicked)="continuar()"> Continuar </tm-button>Lista de permisos
@for (permiso of permisos; track permiso.id) {
<tm-checkbox-button
[checked]="permiso.activo"
[label]="permiso.nombre"
(changed)="togglePermiso(permiso.id, $event)"
/>
}togglePermiso(id: string, value: boolean) {
const permiso = this.permisos.find(p => p.id === id);
if (permiso) permiso.activo = value;
}Con disabled según permisos
<tm-checkbox-button
[checked]="opcion.activa"
[disabled]="!tienePermisos"
[label]="opcion.nombre"
(changed)="opcion.activa = $event"
/>Componente de Chip
Componente para mostrar estados, etiquetas o valores visuales. Puede ser estático o clickeable.
Instalación
import { Chip } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [Chip],
})Uso básico
<tm-chip status="success">Activo</tm-chip>Status (colores)
| Status | Uso recomendado |
| --------- | --------------------------------- |
| success | Activo, completado, aprobado |
| warning | Pendiente, por vencer, precaución |
| danger | Inactivo, error, rechazado |
| info | Informativo, en proceso |
| indigo | Categorías, etiquetas neutras |
| brand | Destacado, principal |
<tm-chip status="success">Activo</tm-chip>
<tm-chip status="warning">Pendiente</tm-chip>
<tm-chip status="danger">Inactivo</tm-chip>
<tm-chip status="info">En proceso</tm-chip>
<tm-chip status="indigo">Categoría</tm-chip>
<tm-chip status="brand">Destacado</tm-chip>Type (estilo visual)
| Type | Descripción |
| ---------- | --------------------------------------- |
| filled | Fondo sólido con texto blanco |
| subtle | Fondo suave con texto del color |
| outlined | Sin fondo, solo borde y texto del color |
<tm-chip status="success" type="filled">Filled</tm-chip>
<tm-chip status="success" type="subtle">Subtle</tm-chip>
<tm-chip status="success" type="outlined">Outlined</tm-chip>Rounded (forma)
| Rounded | Uso recomendado |
| --------- | ---------------------------- |
| default | Estados, badges informativos |
| full | Filtros, tags interactivos |
<tm-chip status="info" rounded="default">Estado</tm-chip>
<tm-chip status="info" rounded="full">Etiqueta</tm-chip>Íconos
Los íconos se importan desde lucide-angular como objetos:
import { Check, TriangleAlert, X } from 'lucide-angular';
export class MiComponente {
check = Check;
alert = TriangleAlert;
x = X;
}<!-- Ícono izquierdo -->
<tm-chip status="success" [iconLeft]="check">Activo</tm-chip>
<!-- Ícono derecho -->
<tm-chip status="info" [iconRight]="x">Etiqueta</tm-chip>
<!-- Ambos íconos -->
<tm-chip status="warning" [iconLeft]="alert" [iconRight]="x">Advertencia</tm-chip>Clickeable
Por defecto el chip es estático. Para hacerlo interactivo usa [clickable]="true" y escucha el evento (clicked):
<tm-chip
status="danger"
type="filled"
rounded="full"
[iconLeft]="x"
[clickable]="true"
(clicked)="onDesactivar()"
>
Desactivar
</tm-chip>onDesactivar() {
console.log('chip clickeado');
}Si
clickableesfalse(default), el chip no emite eventos ni muestra cursor pointer.
API
Inputs
| Input | Tipo | Default | Descripción |
| ----------- | ---------------- | ----------- | ------------------------------- |
| status | ChipStatus | 'brand' | Color del chip |
| type | ChipType | 'filled' | Estilo visual |
| rounded | ChipRounded | 'default' | Forma del borde |
| iconLeft | LucideIconData | — | Ícono a la izquierda |
| iconRight | LucideIconData | — | Ícono a la derecha |
| clickable | boolean | false | Habilita click y cursor pointer |
Outputs
| Output | Tipo | Descripción |
| ----------- | ------ | --------------------------------------------- |
| (clicked) | void | Se emite al hacer click si clickable="true" |
Tipos
type ChipStatus = 'success' | 'warning' | 'indigo' | 'danger' | 'info' | 'brand';
type ChipType = 'filled' | 'subtle' | 'outlined';
type ChipRounded = 'default' | 'full';Casos de uso reales
<!-- Estado de un producto -->
<tm-chip status="success" type="subtle" rounded="full" [iconLeft]="check"> Activo </tm-chip>
<!-- Licencia por vencer -->
<tm-chip status="warning" type="filled" rounded="default" [iconLeft]="alert"> Por vencer </tm-chip>
<!-- Filtro clickeable -->
<tm-chip status="indigo" type="outlined" rounded="full" [clickable]="true" (clicked)="filtrar()">
Electrónica
</tm-chip>
<!-- Notificación de error -->
<tm-chip status="danger" type="subtle" rounded="default" [iconLeft]="x"> Pago rechazado </tm-chip>Componente CloseDialog
Componente de botón de cierre de la librería @10mindssoftware/tm-ui-kit. Diseñado para cerrar diálogos, modales o paneles. Muestra un ícono de X con fondo circular al hacer hover.
Importación
import { CloseDialog } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [CloseDialog],
})API
Inputs
| Prop | Tipo | Default | Descripción |
| ----------- | --------- | ---------- | ------------------------- |
| disabled | boolean | false | Desactiva el botón |
| ariaLabel | string | 'Cerrar' | Texto accesible del botón |
Outputs
| Evento | Tipo | Descripción |
| -------- | ------ | --------------------------------------------------- |
| closed | void | Se emite al hacer click (no emite si está disabled) |
Ejemplos
Uso básico
<tm-close-dialog (closed)="onClose()" />Deshabilitado
<tm-close-dialog [disabled]="true" />Con aria-label personalizado
<tm-close-dialog ariaLabel="Cerrar modal de confirmación" (closed)="onClose()" />Combinaciones comunes
<!-- En la cabecera de un modal -->
<div class="flex items-center justify-between">
<h2>Título del modal</h2>
<tm-close-dialog (closed)="cerrarModal()" />
</div>
<!-- Deshabilitado durante procesamiento -->
<tm-close-dialog [disabled]="procesando" (closed)="cerrarModal()" />procesando = false;
cerrarModal() {
console.log('Cerrando modal...');
}Notas
- El evento
closedno se emite cuandodisabledestrue, no es necesario manejarlo manualmente. - El ícono de cierre es fijo (
Xdelucide-angular) y no es configurable. - El botón tiene forma circular y aplica un fondo sutil (
hover:bg-tm-subtle) al pasar el cursor, sin afectar el layout del contenedor.
Componente InputTen
Componente de campo de texto de la librería @10mindssoftware/tm-ui-kit. Soporta múltiples tipos de input, tamaños, íconos, estados de error, hints y labels.
Uso
Importa el componente en tu módulo o componente standalone:
import { InputTen } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [InputTen],
})API
Inputs
| Prop | Tipo | Default | Descripción |
| ------------- | ---------------- | --------------- | ------------------------------------------------------------ |
| value | string | '' | Valor del input (two-way binding con model) |
| size | InputSize | 'md' | Tamaño del input |
| type | InputType | 'text' | Tipo de input nativo |
| placeholder | string | 'placeholder' | Texto de placeholder |
| label | string | '' | Etiqueta visible sobre el input |
| hint | string | '' | Texto de ayuda debajo del input (no se muestra si hay error) |
| disabled | boolean | false | Desactiva el input |
| required | boolean | false | Marca el campo como requerido (agrega * al label) |
| error | string | '' | Mensaje de error (activa el estado visual de error) |
| iconLeft | LucideIconData | — | Ícono a la izquierda del input |
| iconRight | LucideIconData | — | Ícono a la derecha del input |
Outputs
| Evento | Tipo | Descripción |
| --------- | ------------ | --------------------------------------- |
| focused | FocusEvent | Se emite cuando el input recibe el foco |
| blurred | FocusEvent | Se emite cuando el input pierde el foco |
Tipos
type InputSize = 'sm' | 'md';
type InputType = 'text' | 'email' | 'password' | 'number' | 'search' | 'tel' | 'url';Ejemplos
Uso básico
<tm-input placeholder="Escribe algo..." />Con label y hint
<tm-input
label="Correo electrónico"
placeholder="[email protected]"
hint="Nunca compartiremos tu correo."
/>Campo requerido
<tm-input label="Nombre completo" placeholder="Juan Pérez" [required]="true" />Tamaños
<tm-input size="md" placeholder="Medium (default)" /> <tm-input size="sm" placeholder="Small" />Tipos de input
<tm-input type="text" placeholder="Texto" />
<tm-input type="email" placeholder="Correo" />
<tm-input type="password" placeholder="Contraseña" />
<tm-input type="number" placeholder="Número" />
<tm-input type="search" placeholder="Buscar..." />
<tm-input type="tel" placeholder="Teléfono" />
<tm-input type="url" placeholder="https://..." />Estado deshabilitado
<tm-input [disabled]="true" placeholder="No disponible" />
<tm-input [disabled]="true" label="Usuario" placeholder="admin" />Estado de error
<tm-input
label="Correo electrónico"
placeholder="[email protected]"
error="El correo ingresado no es válido."
/>Cuando
errortiene valor, el hint queda oculto y el borde cambia a rojo.
Con íconos
Los íconos se pasan como objetos LucideIconData, no como strings.
// app.ts
import { Mail, Lock, Search, Eye } from 'lucide-angular';
export class App {
mail = Mail;
lock = Lock;
search = Search;
eye = Eye;
}<!-- Ícono izquierdo -->
<tm-input [iconLeft]="mail" placeholder="Correo electrónico" />
<!-- Ícono derecho -->
<tm-input [iconRight]="eye" type="password" placeholder="Contraseña" />
<!-- Ambos íconos -->
<tm-input [iconLeft]="search" [iconRight]="eye" placeholder="Buscar..." />Two-way binding
<tm-input [(value)]="correo" placeholder="Correo electrónico" />
<p>Valor actual: {{ correo }}</p>correo = '';Manejo de eventos de foco
<tm-input placeholder="Nombre" (focused)="onFocus($event)" (blurred)="onBlur($event)" />onFocus(event: FocusEvent) {
console.log('Input enfocado', event);
}
onBlur(event: FocusEvent) {
console.log('Input desenfocado', event);
}Combinaciones comunes
<!-- Campo de búsqueda -->
<tm-input type="search" size="sm" [iconLeft]="search" placeholder="Buscar usuarios..." />
<!-- Campo de contraseña con label y error -->
<tm-input
type="password"
label="Contraseña"
placeholder="Mínimo 8 caracteres"
[iconLeft]="lock"
error="La contraseña es demasiado corta."
/>
<!-- Campo de email requerido con hint -->
<tm-input
type="email"
label="Correo electrónico"
placeholder="[email protected]"
[iconLeft]="mail"
hint="Te enviaremos un enlace de confirmación."
[required]="true"
[(value)]="email"
/>Notas
- El prop
valueusamodel()de Angular Signals, lo que permite two-way binding con la sintaxis[(value)]. - Cuando
errortiene contenido, elhintno se muestra, independientemente de su valor. - El estado visual del wrapper cambia automáticamente entre
default,active,errorydisabledsin configuración adicional. - Los íconos deben importarse desde
lucide-angularcomo objetos y asignarse a propiedades del componente. Pasar strings directamente no funciona. - No se requiere ninguna configuración global de íconos en
app.config.ts.
Componente de Menú
Componente de menú desplegable flexible para Angular. Soporta cualquier trigger personalizado (avatar, botón, ícono de 3 puntos, etc.) y contenido variable dentro del panel.
Instalación
Importa TmMenu en tu componente:
import { TmMenu } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [TmMenu],
})
export class MiComponente {}
TmMenuya incluyeMenuComponent,MenuTriggerDirectiveyMenuItem— no necesitas importarlos por separado.
Uso básico
<tm-menu placement="bottom-end" triggerOn="click">
<ng-template tmMenuTrigger>
<button>Abrir menú</button>
</ng-template>
<tm-menu-item label="Mi perfil" [icon]="userIcon" />
<tm-menu-item label="Configuración" [icon]="settingsIcon" />
<tm-menu-item label="Cerrar sesión" [danger]="true" [divider]="true" />
</tm-menu>Inputs de tm-menu
| Input | Tipo | Default | Descripción |
| ----------- | ------------------------------------------------------------ | -------------- | ----------------------- |
| placement | 'bottom-start' \| 'bottom-end' \| 'top-start' \| 'top-end' | 'bottom-end' | Posición del panel |
| triggerOn | 'click' \| 'hover' \| 'both' | 'click' | Cómo se dispara el menú |
Outputs de tm-menu
| Output | Descripción |
| ---------- | --------------------------------- |
| (opened) | Se emite cuando el menú se abre |
| (closed) | Se emite cuando el menú se cierra |
Inputs de tm-menu-item
| Input | Tipo | Default | Descripción |
| ---------- | ---------------- | --------- | ----------------------------- |
| label | string | requerido | Texto del ítem |
| icon | LucideIconData | — | Ícono a la izquierda |
| danger | boolean | false | Estilo de color rojo |
| divider | boolean | false | Muestra línea divisora encima |
| disabled | boolean | false | Deshabilita el ítem |
Outputs de tm-menu-item
| Output | Descripción |
| ------------ | -------------------------------------------- |
| (selected) | Se emite al hacer click (respeta disabled) |
Ícono dinámico de abierto/cerrado
Para mostrar un chevron que cambia según el estado del menú, usa los outputs (opened) y (closed) junto con un signal en tu componente:
En tu .ts:
import { signal } from '@angular/core';
import { ChevronDown, ChevronUp } from 'lucide-angular';
isMenuOpen = signal(false);
chevronDown = ChevronDown;
chevronUp = ChevronUp;En tu .html:
<tm-menu placement="bottom-end" (opened)="isMenuOpen.set(true)" (closed)="isMenuOpen.set(false)">
<ng-template tmMenuTrigger>
<div class="flex items-center gap-2">
<span>Jhon Doe</span>
<lucide-icon [img]="isMenuOpen() ? chevronUp : chevronDown" [size]="16" />
</div>
</ng-template>
<tm-menu-item label="Mi perfil" [icon]="userIcon" />
<tm-menu-item label="Cerrar sesión" [danger]="true" />
</tm-menu>Si tienes múltiples menús en el mismo componente, crea un signal separado para cada uno:
isMenu1Open,isMenu2Open, etc.
Menú con header de perfil (caso mobile/especial)
Usa el slot menuHeader para agregar contenido encima de los ítems, como el avatar y nombre del usuario:
<tm-menu placement="bottom-start">
<ng-template tmMenuTrigger>
<img src="avatar.png" class="rounded-full w-10 h-10" />
</ng-template>
<!-- Header opcional -->
<div menuHeader class="px-3 py-3 flex items-center gap-3 border-b border-tm-subtle w-full">
<img src="avatar.png" class="rounded-full w-10 h-10" />
<div>
<p class="font-medium text-sm">Jhon Doe</p>
<p class="text-xs text-tm-secondary">Super Admin</p>
</div>
</div>
<tm-menu-item label="Mi perfil" [icon]="userIcon" />
<tm-menu-item label="Cerrar sesión" [danger]="true" [divider]="true" />
</tm-menu>Menú de acciones en tabla (ícono de 3 puntos)
<tm-menu placement="bottom-end">
<ng-template tmMenuTrigger>
<lucide-icon [img]="dotsIcon" [size]="16" />
</ng-template>
<tm-menu-item label="Editar" [icon]="editIcon" />
<tm-menu-item label="Eliminar" [icon]="trashIcon" [danger]="true" [divider]="true" />
</tm-menu>Divider
El divider muestra una línea separadora encima del ítem. Úsalo en el primer ítem de una sección secundaria, como "Cerrar sesión":
<tm-menu-item label="Cerrar sesión" [danger]="true" [divider]="true" />Componente de Radio Button
Componente de selección única para formularios. Se usa en grupos donde solo una opción puede estar seleccionada a la vez.
Instalación
import { RadioButton } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [RadioButton],
})Uso básico
<tm-radio-button
value="opcion1"
name="mi-grupo"
[checked]="selected === 'opcion1'"
(changed)="selected = $event"
/>API
Inputs
| Input | Tipo | Default | Descripción |
| ---------- | --------- | --------------- | ------------------------------------------------------- |
| value | string | requerido | Valor que emite cuando se selecciona |
| name | string | 'radio-group' | Nombre del grupo, debe ser igual en todos los del grupo |
| checked | boolean | false | Estado actual del radio |
| disabled | boolean | false | Desactiva la interacción |
| label | string | — | Texto descriptivo al lado del radio |
| id | string | auto-generado | ID del input para asociar labels externos |
Outputs
| Output | Tipo | Descripción |
| ----------- | -------- | --------------------------------------- |
| (changed) | string | Emite el value del radio seleccionado |
Estados
<!-- Sin seleccionar -->
<tm-radio-button value="opcion1" name="grupo" [checked]="false" (changed)="onChanged($event)" />
<!-- Seleccionado -->
<tm-radio-button value="opcion1" name="grupo" [checked]="true" (changed)="onChanged($event)" />
<!-- Disabled desmarcado -->
<tm-radio-button value="opcion1" name="grupo" [checked]="false" [disabled]="true" />
<!-- Disabled marcado -->
<tm-radio-button value="opcion1" name="grupo" [checked]="true" [disabled]="true" />Uso en grupo
El name debe ser el mismo en todos los radio del grupo. El padre controla cuál está seleccionado comparando value con el valor actual:
selected = 'mensual';
onChanged(value: string) {
this.selected = value;
}<div class="flex flex-col gap-3">
<tm-radio-button
value="mensual"
name="plan"
label="Mensual"
[checked]="selected === 'mensual'"
(changed)="onChanged($event)"
/>
<tm-radio-button
value="anual"
name="plan"
label="Anual"
[checked]="selected === 'anual'"
(changed)="onChanged($event)"
/>
<tm-radio-button
value="lifetime"
name="plan"
label="De por vida"
[checked]="selected === 'lifetime'"
(changed)="onChanged($event)"
/>
</div>Casos de uso reales
Selección de plan
<div class="flex flex-col gap-3">
@for (plan of planes; track plan.id) {
<tm-radio-button
[value]="plan.id"
name="planes"
[label]="plan.nombre"
[checked]="selectedPlan === plan.id"
(changed)="selectedPlan = $event"
/>
}
</div>Con opción desactivada
<tm-radio-button
value="premium"
name="plan"
label="Premium (próximamente)"
[checked]="false"
[disabled]="true"
/>Selección de rol en formulario
<div class="flex flex-col gap-3">
<tm-radio-button
value="admin"
name="rol"
label="Administrador"
[checked]="rol === 'admin'"
(changed)="rol = $event"
/>
<tm-radio-button
value="editor"
name="rol"
label="Editor"
[checked]="rol === 'editor'"
(changed)="rol = $event"
/>
<tm-radio-button
value="viewer"
name="rol"
label="Solo lectura"
[checked]="rol === 'viewer'"
(changed)="rol = $event"
/>
</div>rol = 'viewer';Componente de Select
Combobox con búsqueda integrada, soporte para avatar, ícono y grupos de opciones. Usa CDK Overlay para funcionar correctamente dentro de modales y tablas.
Instalación
Importa TmSelect en tu componente:
import { TmSelect } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [TmSelect],
})
export class MiComponente {}Uso básico
<div class="w-[273px]">
<tm-select
label="Asignar a"
placeholder="Selecciona una persona"
[options]="myOptions"
(changed)="onSelect($event)"
/>
</div>import { SelectOption } from '@10mindssoftware/tm-ui-kit';
myOptions: SelectOption[] = [
{ value: 1, label: 'Ana García' },
{ value: 2, label: 'Luis Pérez' },
{ value: 3, label: 'Sin asignar', disabled: true },
];
onSelect(option: SelectOption | null) {
console.log('value:', option?.value); // el ID que mandas al backend
}El select ocupa el 100% del contenedor padre. Define el ancho desde afuera con un
divwrapper.
API de inputs
| Input | Tipo | Default | Descripción |
| ------------- | ---------------- | ------------------ | ----------------------------- |
| options | SelectOption[] | [] | Lista de opciones |
| placeholder | string | 'Seleccionar...' | Texto cuando no hay selección |
| size | 'sm' \| 'md' | 'md' | Tamaño del campo |
| label | string | '' | Etiqueta encima del campo |
| disabled | boolean | false | Deshabilita el select |
| error | string | '' | Mensaje de error |
API de outputs
| Output | Tipo | Descripción |
| ----------- | ---------------------- | --------------------------------- |
| (changed) | SelectOption \| null | Se emite al seleccionar o limpiar |
Model
| Model | Tipo | Descripción |
| ------- | -------------------------- | ---------------------------------------------- |
| value | string \| number \| null | Valor seleccionado (bindeable con [(value)]) |
Ejemplo con avatar e ícono
Cada opción puede tener un avatar (imagen) o un icon (Lucide). Si tiene ambos, el avatar tiene prioridad.
import { User, Package } from 'lucide-angular';
myOptions: SelectOption[] = [
{ value: 1, label: 'Ana García', avatar: 'https://ejemplo.com/ana.jpg' },
{ value: 2, label: 'Luis Pérez', icon: User },
{ value: 3, label: 'Sin asignar', disabled: true },
];<div class="w-[273px]">
<tm-select
label="Responsable"
placeholder="Selecciona una persona"
[options]="myOptions"
(changed)="onSelect($event)"
/>
</div>Ejemplo con grupos
Agrega la propiedad group a cada opción para agruparlas con un encabezado:
myOptions: SelectOption[] = [
{ value: 1, label: 'Ana García', avatar: 'https://...', group: 'Equipo A' },
{ value: 2, label: 'Luis Pérez', icon: User, group: 'Equipo A' },
{ value: 3, label: 'Sin asignar', group: 'Equipo B', disabled: true },
];Las opciones sin group se muestran sin encabezado.
Ejemplo con error y disabled
<div class="w-[273px] flex flex-col gap-4">
<!-- Con error -->
<tm-select
label="Asignar a"
[options]="myOptions"
error="Este campo es requerido"
(changed)="onSelect($event)"
/>
<!-- Deshabilitado -->
<tm-select label="Asignar a" [options]="myOptions" [disabled]="true" />
</div>Obtener el valor para el backend
El value de cada opción es el ID que usas para mandar al backend:
onSelect(option: SelectOption | null) {
const payload = {
asignado_a: option?.value ?? null
};
this.api.post('/tarea', payload).subscribe();
}Interface SelectOption
export interface SelectOption {
value: string | number;
label: string;
avatar?: string;
icon?: LucideIconData;
group?: string;
disabled?: boolean;
}Componente SnackbarService
Sistema de notificaciones globales para mostrar feedback visual al usuario.
Configuración (una sola vez)
app.html:
<snackbar-host /> <router-outlet />app.ts:
import { SnackbarHost } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [SnackbarHost],
})Uso
import { SnackbarService } from '@10mindssoftware/tm-ui-kit';
export class MiComponente {
snackbar = inject(SnackbarService);
async onGuardar() {
try {
await this.service.guardar(data);
this.snackbar.alert('success', 'Guardado', 'Los cambios fueron guardados correctamente');
} catch {
this.snackbar.alert('error', 'Error', 'No pudimos guardar los cambios');
}
}
}Métodos
| Método | Descripción |
| --------------------------------------------------------- | -------------------------------- |
| alert(color, title, description?, duration?, position?) | Desaparece automáticamente |
| notify(color, title, description?, position?) | El usuario la cierra manualmente |
Parámetros
| Parámetro | Tipo | Default | Valores |
| ------------- | ------------------ | ----------- | -------------------------------------------------------------------------------- |
| color | SnackbarColor | — | success error warning info |
| title | string | — | — |
| description | string | undefined | — |
| duration | number | 5000 | Milisegundos |
| position | SnackbarPosition | top-right | top-left top-center top-right bottom-left bottom-center bottom-right |
Ejemplos
// Mínimo
this.snackbar.alert('success', 'Guardado');
// Con descripción
this.snackbar.alert('error', 'Error de pago', 'No pudimos procesar tu método de pago');
// Con duración personalizada
this.snackbar.alert('warning', 'Licencia por expirar', 'Expira en 7 días', 2000);
// Con posición personalizada
this.snackbar.alert('info', 'Nueva versión', 'Yana 2.0 disponible', 5000, 'bottom-center');
// Notificación persistente
this.snackbar.notify('success', 'Compra exitosa', 'Tu licencia fue activada');
// Notificación con posición
this.snackbar.notify('error', 'Sin conexión', undefined, 'bottom-right');Componente Toggle
Componente de interruptor para activar o desactivar estados. Emite el nuevo valor al padre para que maneje la lógica correspondiente.
Instalación
import { Toggle } from '@10mindssoftware/tm-ui-kit';
@Component({
imports: [Toggle],
})Uso básico
<tm-toggle [value]="isActive" (changed)="isActive = $event" />API
Inputs
| Input | Tipo | Default | Descripción |
| ---------- | --------- | ------- | ------------------------ |
| value | boolean | false | Estado actual del toggle |
| disabled | boolean | false | Desactiva la interacción |
Outputs
| Output | Tipo | Descripción |
| ----------- | --------- | ------------------------------------ |
| (changed) | boolean | Emite el nuevo estado al hacer click |
Estados
<!-- Apagado -->
<tm-toggle [value]="false" (changed)="onChanged($event)" />
<!-- Encendido -->
<tm-toggle [value]="true" (changed)="onChanged($event)" />
<!-- Disabled apagado -->
<tm-toggle [value]="false" [disabled]="true" />
<!-- Disabled encendido -->
<tm-toggle [value]="true" [disabled]="true" />Manejo del estado
El toggle no guarda estado internamente. El padre es responsable de actualizar el valor:
isActive = false;
onChanged(value: boolean) {
this.isActive = value;
}<tm-toggle [value]="isActive" (changed)="onChanged($event)" />O de forma inline:
<tm-toggle [value]="isActive" (changed)="isActive = $event" />Casos de uso reales
Activar notificaciones
<div class="flex items-center gap-3">
<tm-toggle [value]="notificaciones" (changed)="notificaciones = $event" />
<span>Notificaciones</span>
</div>
@if (notificaciones) {
<p>Recibirás notificaciones de nuevos eventos.</p>
} @else {
<p>Las notificaciones están desactivadas.</p>
}Activar/desactivar un producto
<div class="flex items-center gap-3">
<tm-toggle [value]="producto.activo" (changed)="toggleProducto($event)" />
<span>{{ producto.activo ? 'Activo' : 'Inactivo' }}</span>
</div>toggleProducto(value: boolean) {
this.producto.activo = value;
this.productoService.actualizarEstado(this.producto.id, value);
}Con disabled según permisos
<tm-toggle
[value]="configuracion.habilitado"
[disabled]="!tienePermisos"
(changed)="configuracion.habilitado = $event"
/>