Fab
fabuiFloating action button — a fixed or absolute positioned circular button for the primary screen action. Supports an icon (default), an extended label variant, mini/large sizes, four color variants, six anchor positions, and a disabled state.
Installation
$pnpm dlx uipkge-ng@latest add fab$npx uipkge-ng@latest add fab$yarn dlx uipkge-ng@latest add fab$bunx uipkge-ng@latest add fabManifest:
https://uipkge.dev/r/angular/fab.jsonInstalls to:src/app/components/ui/fab/Examples
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
variant | 'default''secondary''destructive''outline' | default | optional |
size | 'mini''default''large''extended' | default | optional |
position | 'bottom-right''bottom-left''top-right''top-left''bottom-center''inline' | bottom-right | optional |
labelLabel text: renders an extended FAB. Project the icon as content. | string | — | optional |
absoluteAbsolute instead of fixed positioning (for contained FABs). | boolean | false | optional |
disabled | boolean | false | optional |
ariaLabelAccessible label. Defaults to `label`, then 'Floating action'. | string | — | optional |
aria-labelA plain `aria-label="…"` attribute wins over `ariaLabel`, like React. | string | — | optional |
class | string | — | optional |
npm dependencies
Used by
Files installed (3)
src/app/components/ui/fab/fab.component.ts2.8 kB
import { Component, ElementRef, Input, booleanAttribute, inject, ChangeDetectionStrategy } from '@angular/core' import { cn } from '@/lib/utils' import { fabVariants, type FabVariants } from './fab.variants' export type FabVariant = NonNullable<FabVariants['variant']> export type FabSize = NonNullable<FabVariants['size']> export type FabPosition = NonNullable<FabVariants['position']> /** * Angular port of UIPKGE Fab (React `Fab`): a floating action <button>. Put it on a native * element — `<button ui-fab>` (React default) or `<a ui-fab>` (React `asChild` with a link). * Click with the native `(click)`; while `disabled` clicks are swallowed (the <button> also gets * the native disabled attribute). Class strings identical to React via shared `fabVariants`. */ @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'button[ui-fab], a[ui-fab]', standalone: true, host: { '[attr.data-uipkge]': '""', '[attr.data-slot]': '"fab"', '[attr.data-variant]': 'variant ?? null', '[attr.data-size]': 'resolvedSize ?? null', '[attr.data-position]': 'position ?? null', '[attr.disabled]': 'disabled && isButton ? "" : null', '[attr.aria-label]': 'ariaLabelAttr || ariaLabel || label || "Floating action"', '[class]': 'hostClass', }, template: `<ng-content /> @if (label) { <span class="pr-1">{{ label }}</span> }`, }) export class UiFabComponent { private readonly el = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement /** Label text: renders an extended FAB. Project the icon as content. */ @Input() label?: string @Input() variant?: FabVariant @Input() size?: FabSize @Input() position?: FabPosition /** Absolute instead of fixed positioning (for contained FABs). */ @Input({ transform: booleanAttribute }) absolute = false @Input({ transform: booleanAttribute }) disabled = false /** Accessible label. Defaults to `label`, then 'Floating action'. */ @Input() ariaLabel?: string /** A plain `aria-label="…"` attribute wins over `ariaLabel`, like React. */ @Input('aria-label') ariaLabelAttr?: string @Input('class') className?: string constructor() { // Capture phase so it runs before consumer (click) handlers: disabled <a> FABs must not fire. this.el.addEventListener( 'click', (e) => { if (!this.disabled) return e.preventDefault() e.stopImmediatePropagation() }, true, ) } get isButton(): boolean { return this.el.tagName === 'BUTTON' } get resolvedSize(): FabSize | undefined { return this.label ? 'extended' : this.size } get hostClass(): string { return cn( fabVariants({ variant: this.variant, size: this.resolvedSize, position: this.position }), this.absolute && this.position !== 'inline' && 'absolute', this.className, ) } } export { fabVariants, type FabVariants }src/app/components/ui/fab/fab.variants.ts1.6 kB
import type { VariantProps } from 'class-variance-authority' import { cva } from 'class-variance-authority' export const fabVariants = cva( "inline-flex items-center justify-center gap-2 rounded-full font-medium shadow-lg transition-[color,background-color,box-shadow,transform,scale] duration-200 outline-none focus-visible:ring-ring/50 focus-visible:ring-[3px] active:scale-95 touch-manipulation [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-6 disabled:pointer-events-none disabled:opacity-50", { variants: { variant: { default: 'bg-primary text-primary-foreground hover:bg-primary/90', secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', destructive: 'bg-destructive text-white hover:bg-destructive/90', outline: 'border bg-background text-foreground hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', }, size: { mini: "size-10 [&_svg:not([class*='size-'])]:size-4", default: 'size-14', large: "size-16 [&_svg:not([class*='size-'])]:size-7", extended: 'h-14 px-5', }, position: { 'bottom-right': 'fixed bottom-6 right-6', 'bottom-left': 'fixed bottom-6 left-6', 'top-right': 'fixed top-6 right-6', 'top-left': 'fixed top-6 left-6', 'bottom-center': 'fixed bottom-6 left-1/2 -translate-x-1/2', inline: '', }, }, defaultVariants: { variant: 'default', size: 'default', position: 'bottom-right', }, }, ) export type FabVariants = VariantProps<typeof fabVariants>src/app/components/ui/fab/index.ts0.1 kB
export { UiFabComponent, fabVariants, type FabVariants, type FabVariant, type FabSize, type FabPosition, } from './fab.component'
Raw manifest:https://uipkge.dev/r/angular/fab.json