Run the CLI
Use the CLI to add the component to your project.
npx @ngzard/ui add buttonDisplays a button or a component that looks like a button.
import { Component } from '@angular/core';
import { ZardIconComponent } from '../../icon/icon.component';
import { ZardButtonComponent } from '../button.component';
@Component({
selector: 'z-demo-button-default',
standalone: true,
imports: [ZardButtonComponent, ZardIconComponent],
template: `
<button z-button zType="outline">Button</button>
<button z-button zType="outline"><i z-icon zType="arrow-up"></i></button>
<button z-button zType="outline">Button <i z-icon zType="popcorn"></i></button>
`,
})
export class ZardDemoButtonDefaultComponent {}
Use the CLI to add the component to your project.
npx @ngzard/ui add buttonpnpm dlx @ngzard/ui add buttonyarn dlx @ngzard/ui add buttonbunx @ngzard/ui add buttonCreate the component directory structure and add the following files to your project.
import { afterNextRender, ChangeDetectionStrategy, Component, computed, type OnDestroy, ElementRef, inject, input, signal, ViewEncapsulation } from '@angular/core';
import type { ClassValue } from 'clsx';
import { buttonVariants, type ZardButtonVariants } from './button.variants';
import { mergeClasses, transform } from '../../shared/utils/utils';
import { ZardIconComponent } from '../icon/icon.component';
@Component({
selector: 'z-button, button[z-button], a[z-button]',
exportAs: 'zButton',
standalone: true,
imports: [ZardIconComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
template: `
@if (zLoading()) {
<i z-icon zType="loader-circle" class="animate-spin duration-2000"></i>
}
<ng-content></ng-content>
`,
host: {
'[class]': 'classes()',
'[attr.data-icon-only]': 'iconOnly() || null',
},
})
export class ZardButtonComponent implements OnDestroy {
private readonly elementRef = inject(ElementRef<HTMLElement>);
readonly zType = input<ZardButtonVariants['zType']>('default');
readonly zSize = input<ZardButtonVariants['zSize']>('default');
readonly zShape = input<ZardButtonVariants['zShape']>('default');
readonly class = input<ClassValue>('');
readonly zFull = input(false, { transform });
readonly zLoading = input(false, { transform });
private readonly iconOnlyState = signal(false);
readonly iconOnly = this.iconOnlyState.asReadonly();
private _mutationObserver: MutationObserver | null = null;
constructor() {
afterNextRender(() => {
const check = () => {
const el = this.elementRef.nativeElement;
const hasIcon = el.querySelector('z-icon, [z-icon]') !== null;
const children = Array.from<Node>(el.childNodes);
const hasText = children.some(node => {
if (node.nodeType === 3) {
return node.textContent?.trim() !== '';
}
if (node.nodeType === 1) {
const element = node as HTMLElement;
if (element.matches('z-icon, [z-icon]')) return false;
return element.textContent?.trim() !== '';
}
return false;
});
this.iconOnlyState.set(hasIcon && !hasText);
};
check();
this._mutationObserver = new MutationObserver(check);
this._mutationObserver.observe(this.elementRef.nativeElement, { childList: true, characterData: true, subtree: true });
});
}
ngOnDestroy(): void {
if (this._mutationObserver) {
this._mutationObserver.disconnect();
this._mutationObserver = null;
}
}
protected readonly classes = computed(() =>
mergeClasses(
buttonVariants({
zType: this.zType(),
zSize: this.zSize(),
zShape: this.zShape(),
zFull: this.zFull(),
zLoading: this.zLoading(),
}),
this.class(),
),
);
}
import { cva, type VariantProps } from 'class-variance-authority';
export const buttonVariants = cva(
"cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all active:scale-97 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
zType: {
default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
destructive: 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
outline: 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
link: 'text-primary underline-offset-4 hover:underline',
},
zSize: {
default: 'h-9 px-4 py-2 data-[icon-only]:size-9 data-[icon-only]:p-0',
sm: 'h-8 rounded-md gap-1.5 px-3 data-[icon-only]:size-8 data-[icon-only]:p-0',
lg: 'h-10 rounded-md px-6 data-[icon-only]:size-10 data-[icon-only]:p-0',
},
zShape: {
default: 'rounded-md',
circle: 'rounded-full',
square: 'rounded-none',
},
zFull: {
true: 'w-full',
},
zLoading: {
true: 'opacity-50 pointer-events-none',
},
},
defaultVariants: {
zType: 'default',
zSize: 'default',
zShape: 'default',
},
},
);
export type ZardButtonVariants = VariantProps<typeof buttonVariants>;
import { Component } from '@angular/core';
import { ZardIconComponent } from '../../icon/icon.component';
import { ZardButtonComponent } from '../button.component';
@Component({
selector: 'z-demo-button-default',
standalone: true,
imports: [ZardButtonComponent, ZardIconComponent],
template: `
<button z-button zType="outline">Button</button>
<button z-button zType="outline"><i z-icon zType="arrow-up"></i></button>
<button z-button zType="outline">Button <i z-icon zType="popcorn"></i></button>
`,
})
export class ZardDemoButtonDefaultComponent {}
import { Component } from '@angular/core';
import { ZardButtonComponent } from '../button.component';
@Component({
selector: 'z-demo-button-type',
standalone: true,
imports: [ZardButtonComponent],
template: `
<button z-button zSize="sm">Default</button>
<button z-button zSize="sm" zType="outline">Outline</button>
<button z-button zSize="sm" zType="destructive">Destructive</button>
<button z-button zSize="sm" zType="secondary">Secondary</button>
<button z-button zSize="sm" zType="ghost">Ghost</button>
<button z-button zSize="sm" zType="link">Link</button>
`,
host: {
class: 'flex flex-col items-center gap-4 md:flex-row md:gap-8',
},
})
export class ZardDemoButtonTypeComponent {}
import { Component } from '@angular/core';
import { ZardIconComponent } from '../../icon/icon.component';
import { ZardButtonComponent } from '../button.component';
@Component({
selector: 'z-demo-button-size',
standalone: true,
imports: [ZardButtonComponent, ZardIconComponent],
template: `
<div class="flex flex-col items-center">
<div class="mb-4 flex gap-2">
<button z-button zSize="sm">Small</button>
<button z-button zSize="sm"><z-icon zType="arrow-up"></z-icon></button>
</div>
<div class="mb-4 flex gap-2">
<button z-button>Default</button>
<button z-button><z-icon zType="arrow-up"></z-icon></button>
</div>
<div class="mb-4 flex gap-2">
<button z-button zSize="lg">Large</button>
<button z-button zSize="lg"><z-icon zType="arrow-up"></z-icon></button>
</div>
</div>
`,
})
export class ZardDemoButtonSizeComponent {}
import { Component } from '@angular/core';
import { ZardButtonComponent } from '../button.component';
@Component({
selector: 'z-demo-button-shape',
standalone: true,
imports: [ZardButtonComponent],
template: `
<button z-button>Default</button>
<button z-button zShape="circle">Circle</button>
<button z-button zShape="square">Square</button>
`,
})
export class ZardDemoButtonShapeComponent {}
import { Component } from '@angular/core';
import { ZardButtonComponent } from '../button.component';
@Component({
selector: 'z-demo-button-full',
standalone: true,
imports: [ZardButtonComponent],
template: ` <button z-button zFull>Default</button> `,
})
export class ZardDemoButtonFullComponent {}
import { Component } from '@angular/core';
import { ZardButtonComponent } from '../button.component';
@Component({
selector: 'z-demo-button-loading',
standalone: true,
imports: [ZardButtonComponent],
template: ` <button z-button zLoading>Default</button> `,
})
export class ZardDemoButtonLoadingComponent {}