Saltar al contenido principal

button-icon-cover

Paquete: @tiendana/ngx-core/ui

Componente de animación con canvas para botones con icono. Dibuja un cuadrado redondeado que se expande y se contrae con efecto spring cuando el botón padre recibe hover o está marcado como activo.

Características​

✅ Animación con spring physics (masa, rigidez, amortiguamiento)
✅ Renderizado con canvas HTML5 y ajuste al devicePixelRatio
✅ En dispositivos touch permanece siempre expandido
✅ Soporte para botones pequeños (.bttn--small)
✅ Compatible con Material Icons (matIcon) y SVG (svgIcon)
✅ Se repinta al cambiar la clase scheme-* del <body> (por ejemplo .scheme-dark)
✅ Puede ser la raíz de un bloque @if/@else dentro del botón, cediendo su lugar a tdn-busy-loader

API​

Inputs​

InputTipoDefaultDescripción
svgIconInputSignal<string>''Nombre de un icono registrado en el sprite de MatIconRegistry
matIconInputSignal<string>''Ligadura de Material, por ejemplo 'check'. Se dibuja antes que svgIcon si se pasan ambos
activeInputSignal<boolean>falseMantiene el cuadrado expandido como si hubiera hover (por ejemplo mientras un menú abierto por el botón sigue visible)

Signals​

SignalTipoDescripción
isExpandedSignal<boolean>true si es touch, hay hover en el botón padre o active está activo
canvasSignal<ElementRef<HTMLCanvasElement>>Referencia al canvas de dibujo (viewChild.required)

Comportamiento​

  • El cuadrado crece (32px, 16px en botones pequeños) mientras el botón padre tiene hover o active está activo, y reposa pequeño (10px, 5px en botones pequeños) en cualquier otro caso. En un dispositivo touch siempre está expandido.
  • El hover se lee de mouseenter y mouseleave del elemento padre, así que el componente debe ser hijo directo del botón.
  • La detección del hover se conecta después del primer render. Por eso también funciona como raíz de un bloque de control de flujo dentro del botón.
  • Si el componente vuelve a insertarse bajo un puntero en reposo, el estado inicial se toma de :hover, sin esperar un mouseenter.
  • Al destruirse, el componente retira sus listeners y cancela la animación en curso: un botón que lo recrea en cada petición no los acumula.
private springParams = { mass: 1, stiffness: 300, damping: 20 };

targetSize = expanded ? (small ? 16 : 32) : (small ? 5 : 10);
.bttn--small se lee una sola vez

La clase .bttn--small del padre se lee una sola vez, después del primer render. Si la añades o la quitas en un botón ya renderizado, el CSS del host cambia, pero el canvas conserva el tamaño y el dibujo iniciales, así que el cuadrado queda descuadrado.

Variables CSS​

VariableDescripciónDefault
--bttn-icon-canvas-colorRelleno del cuadrado. Se lee del estilo computado del componente en el momento de dibujar--color-dark-forrest de la raíz
--bttn-icon-colorColor del icono (mat-icon)var(--color-trust)
button-icon-cover {
--bttn-icon-canvas-color: var(--color-natura);
}

Tamaños​

VarianteHostCanvasCuadrado expandidoCuadrado en reposoRadioIcono
Normal21x32px32x32px32px10px10px20px
.bttn--small16x16px16x16px16px5px5px12px

Uso​

<button class="bttn bttn--icon">
<button-icon-cover matIcon="delete" />
</button>

<button class="bttn bttn--icon bttn--small">
<button-icon-cover matIcon="add" />
</button>

Con un menú o estado activo​

<button class="bttn" (click)="isOpen.set(!isOpen())">
<button-icon-cover svgIcon="arrow_right" [active]="isOpen()" />
</button>

Cediendo su lugar al loader de ocupado​

Mientras corre una petición, el icono se reemplaza por tdn-busy-loader con place="cover", que conserva la huella del cover para que el botón no cambie de tamaño ni mueva su texto.

<button type="button" class="bttn bttn--primary" [attr.aria-busy]="saving()" (click)="save()">
Guardar
@if (saving()) {
<tdn-busy-loader place="cover" ground="natura" />
} @else {
<button-icon-cover matIcon="check" />
}
</button>

Material Icon o SVG​

<!-- Material Icon -->
<button-icon-cover matIcon="favorite" />

<!-- SVG registrado en MatIconRegistry -->
<button-icon-cover svgIcon="custom-icon" />

Host Classes​

host: {
'[class.expanded]': 'isExpanded()',
'[class.active]': 'active()'
}

Accesibilidad​

  • El canvas y el icono son decorativos: el nombre accesible del botón debe venir de su texto o de un aria-label.
  • No afecta la accesibilidad del botón padre.
  • La animación no consulta prefers-reduced-motion. Si tu producto lo exige, evita el componente o sustitúyelo para esos usuarios.