Progress Bars
The <smart-progress-bars> component renders either a list of progress steps (with statuses complete, current, upcoming) or a percentage-based bar with optional column labels. It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. The abstract ProgressBarsBaseComponent defines the shared API — options (IProgressBarsOptions), cssClass (alias class), and the stepClick output. ProgressBarsStandardComponent is a barebones placeholder using native HTML. ProgressBarsComponent is the public wrapper that renders ProgressBarsStandardComponent by default and accepts a custom replacement via PROGRESS_BARS_STANDARD_COMPONENT_TOKEN.
Usage
<smart-progress-bars [options]="options" (stepClick)="onStepClick($event)" />
Components
ProgressBarsComponent (<smart-progress-bars>)
Main wrapper. Delegates to ProgressBarsStandardComponent by default. When PROGRESS_BARS_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet. Re-emits stepClick.
ProgressBarsStandardComponent (<smart-progress-bars-standard>)
Barebones placeholder. Two rendering modes selected by options.layout:
Step mode (default; layouts simple, panels, bullets, panels-with-border, circles, bullets-and-text, circles-with-text): Renders <nav class="progress-bars"> (with aria-label from options.ariaLabel or default "Progress") containing an <ol> of steps. The data-layout attribute reflects options.layout so each visual variant can be styled. Each step renders as <a class="progress-bars-step-link"> (when href provided) or <button class="progress-bars-step-button"> (otherwise emits stepClick). Steps get current class and aria-current="step" when status === 'current'. The data-status attribute on the <li> reflects status (defaults to 'upcoming'). Supports iconTpl, index (e.g. "02"), name, and description.
Bar mode (layout progress-bar): Renders <div class="progress-bars-bar-wrapper"> with optional <h4 class="sr-only"> (from srOnlyTitle), optional <p class="progress-bars-title"> (from title), a track + <div class="progress-bars-fill" role="progressbar"> (width = clamped value 0–100), and optional <div class="progress-bars-columns"> row of column labels (each with active class when column.active === true).
ProgressBarsPresetComponent (<smart-progress-bars-preset>)
Fully-styled variation that extends ProgressBarsBaseComponent and is a drop-in replacement for ProgressBarsStandardComponent. Register it via PROGRESS_BARS_STANDARD_COMPONENT_TOKEN to restyle every <smart-progress-bars>, or use the <smart-progress-bars-preset> selector directly. It renders both modes off the shared IProgressBarsOptions API:
- Percentage bar (
layout: 'progress-bar'): a rounded track + animatedbg-blue-600fill sized to the clampedvalue(0–100). Shows a title header with avalue%label whentitleis set, ansr-only<h4>fromsrOnlyTitle, and a grid of column captions fromcolumns(active columns rendered semibold). - Stepper (all other layouts): a
<nav>/<ol>of steps. Markers adapt to the layout — numbered/check circles forcircles,circles-with-text,panels,panels-with-border; small dots forbullets,bullets-and-text; a colored top border (no marker) forsimple.bullets-and-textandcircles-with-textlay out vertically; the other step layouts lay out horizontally, with connector lines between markers forcircles/bullets. Completed circle steps show a check SVG; current/upcoming circle steps showstep.index(falling back to the 1-based position). Each step renders an<a>whenstep.hrefis set, otherwise a<button>that emitsstepClick. Names/markers tint bystatus(complete/current/upcoming).
All classes are smart:-prefixed Tailwind with explicit dark: variants. The class recipes live in preset/preset-classes.util.ts (kept out of the public barrel; every export is ProgressBars-prefixed).
Because
ProgressBarsComponentrenders injected components viaNgComponentOutlet(which passes inputs by canonical name),ProgressBarsPresetComponentoverridescssClassasinput<string>('')without theclassalias. Bind it as[cssClass]when using the<smart-progress-bars-preset>selector directly, or just passclasson<smart-progress-bars>(the wrapper forwards it).
ProgressBarsBaseComponent (abstract)
Abstract base directive. Exposes:
options: InputSignal<IProgressBarsOptions | undefined>cssClass: InputSignal<string>(aliasclass)stepClick: OutputEmitterRef<IProgressStepClick>
IProgressStepClick = { stepId: string }.
API
Inputs
| Input | Type | Default | Description |
|---|---|---|---|
options | InputSignal<IProgressBarsOptions | undefined> | - | Progress configuration |
class | InputSignal<string> | '' | External CSS classes (alias for cssClass) |
Outputs
| Output | Type | Description |
|---|---|---|
stepClick | OutputEmitterRef<IProgressStepClick> | Emitted when a button-type step is clicked |
Extending the base class
import {
ChangeDetectionStrategy,
Component,
computed,
input,
ViewEncapsulation,
} from '@angular/core';
import {
IProgressBarsOptions,
PROGRESS_BARS_STANDARD_COMPONENT_TOKEN,
ProgressBarsBaseComponent,
ProgressBarsComponent,
} from '@smartsoft001/angular';
@Component({
selector: 'docs-custom-progress-bars',
template: `
<div [class]="containerClasses()" [attr.aria-label]="options()?.ariaLabel">
@if (options()?.title) {
<p class="docs-progress-bars__title">{{ options()?.title }}</p>
}
@if (options()?.value !== undefined) {
<div class="docs-progress-bars__track">
<div
class="docs-progress-bars__value"
[style.width.%]="options()?.value"
></div>
</div>
}
<ol class="docs-progress-bars__steps">
@for (step of steps(); track step.id) {
<li>
<button
type="button"
class="docs-progress-bars__step"
[class]="
'docs-progress-bars__step--' + (step.status ?? 'upcoming')
"
[attr.aria-current]="step.status === 'current' ? 'step' : null"
(click)="stepClick.emit({ stepId: step.id })"
>
<span class="docs-progress-bars__index">{{ step.index }}</span>
<span class="docs-progress-bars__name">{{ step.name }}</span>
</button>
</li>
}
</ol>
</div>
`,
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomProgressBarsComponent extends ProgressBarsBaseComponent {
// NgComponentOutlet passes 'cssClass' by canonical name, not the 'class' alias.
override cssClass = input<string>('');
readonly steps = computed(() => this.options()?.steps ?? []);
readonly containerClasses = computed(() => {
const classes = ['docs-progress-bars'];
const extra = this.cssClass();
if (extra) classes.push(extra);
return classes.join(' ');
});
}
@Component({
selector: 'docs-progress-bars-custom-example',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ProgressBarsComponent],
// The token swaps the standard progress bars for the custom one everywhere
// below this component, so consumers keep writing `<smart-progress-bars>`.
providers: [
{
provide: PROGRESS_BARS_STANDARD_COMPONENT_TOKEN,
useValue: CustomProgressBarsComponent,
},
],
template: ` <smart-progress-bars [options]="options" /> `,
})
export class ProgressBarsCustomExampleComponent {
readonly options: IProgressBarsOptions = {
layout: 'progress-bar',
title: 'Uploading files',
ariaLabel: 'Upload progress',
value: 50,
steps: [
{ id: 'account', name: 'Account', index: '1', status: 'complete' },
{ id: 'profile', name: 'Profile', index: '2', status: 'current' },
{ id: 'review', name: 'Review', index: '3', status: 'upcoming' },
],
};
}
Source
The component lives in packages/shared/angular/src/lib/components/progress-bars and is documented for Claude Code by the angular-components-progress-bars skill.