Skip to content

Lucapiciollo/loadingTrace

Repository files navigation

⚡ pl-loading-trace

The Angular loading overlay you actually want to use.

Zero boilerplate · 76 built-in animations · HTTP auto-tracking · Router tracking · Named overlays · Determinate progress bars · Runtime config API · Angular Signals

npm version Angular License: MIT Bundle size

Try every animation, tweak config live, trigger HTTP / router events — all in the browser.

Demo preview

👆 Click the image to open the live demo


Table of Contents


1. Why pl-loading-trace?

Most loading libraries make you choose: either a full-screen spinner or a progress bar or per-component loaders. pl-loading-trace does all three, with a single component and a single config.

Feature pl-loading-trace Typical spinner lib
HTTP auto-tracking (functional interceptor)
Router navigation tracking
Named overlays (per-card, per-button)
76 built-in CSS animations ~5
23 determinate progress bar types
Runtime config override via service
Angular 19 Signals-native
Zero dependencies
Tree-shakeable ⚠️

2. Requirements

Package Version
@angular/core ^19.2.0
@angular/common ^19.2.0
@angular/router compatible with Angular 19

Router tracking requires provideRouter(routes) (or RouterModule). If your app has no router, set enableRouterTracer: false.


3. Installation

npm install pl-loading-trace

4. Setup

4.1 Standalone app (Angular 17+)

app.config.ts

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { providePlLoadingTrace, plLoadingHttpInterceptor } from 'pl-loading-trace';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(withInterceptors([plLoadingHttpInterceptor])),
    providePlLoadingTrace({
      shared: { debounceMs: 200 },
      http:   { animationType: 'spinner', modal: true },
      router: { animationType: 'bar' },
    }),
  ],
};

plLoadingHttpInterceptor is a functional interceptor (Angular 15+ style). For class-based interceptors, use PlLoadingHttpInterceptor with HTTP_INTERCEPTORS.

4.2 NgModule app

app.module.ts

import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { PlLoadingTraceModule, PlLoadingHttpInterceptor } from 'pl-loading-trace';

@NgModule({
  imports: [
    HttpClientModule,
    PlLoadingTraceModule.forRoot({
      shared: { debounceMs: 200 },
      http:   { animationType: 'spinner', modal: true },
      router: { animationType: 'bar' },
    }),
  ],
  providers: [
    {
      provide:  HTTP_INTERCEPTORS,
      useClass: PlLoadingHttpInterceptor,
      multi:    true,
    },
  ],
})
export class AppModule {}

5. Quick start

Drop <pl-loading-overlay> once in your root layout — that's it:

<!-- app.component.html -->

<!-- Top-of-page router bar -->
<pl-loading-overlay source="router" />

<!-- Full-screen HTTP overlay -->
<pl-loading-overlay source="http" />

<router-outlet />

Every HttpClient request and every router navigation will now trigger the appropriate overlay automatically.


6. Component inputs

Input Type Default Description
source LoadingSource 'http' Which stream triggers this overlay
contained boolean false Fill nearest position: relative ancestor instead of the viewport
name string '' Register in PlLoadingRegistryService for manual/imperative control
config Partial<PlLoadingTraceConfig> {} Per-instance config (merged over global)
progress number | null null Determinate progress value 0–100 (takes priority over config.progressValue)

source values

Value Triggers when…
'http' Any tracked HttpClient request is pending
'router' A router navigation is in progress
'both' Either HTTP or router is active
'none' Only via PlLoadingRegistryService — fully manual

7. Configuration reference

7.1 PlLoadingTraceConfig — full reference

All properties are optional. Any omitted key falls back to its default.

Property Type Default Description
enableHttpTracer boolean true Intercept HttpClient requests
enableRouterTracer boolean true Track router navigations
animationType AnimationType 'spinner' CSS animation to display (see §8)
animationSpeed number 1000 Animation cycle duration in ms
position LoadingPosition 'center' Where to place the spinner (ignored for bar type)
modal boolean true Show a semi-transparent backdrop
backdropColor string 'rgba(0,0,0,0.55)' Backdrop background (any CSS color)
backdropBlur number 2 Backdrop backdrop-filter: blur(Npx)
color string '#3498db' Primary animation color
size number 40 Animation size in px
spinnerOpacity number 1 Animation element opacity 0–1
barHeight number 5 Track height in px (for bar type)
barColorStart string (same as color) Gradient start color for bar
barColorEnd string '#2ecc71' Gradient end color for bar
label string '' Text shown below the animation
labelColor string (auto-contrast) Label color; auto = white on modal, color on no-modal
debounceMs number 150 Start-only debounce in ms — requests shorter than this won't show the overlay. Configurable independently per source via http/router sections.
excludeUrls string[] [] URL substrings to skip in HTTP tracking
customGifUrl string '' GIF/image to use instead of CSS animation
zIndex number 9999 CSS z-index
wrapperBg string 'transparent' Spinner card background
wrapperRadius number 12 Spinner card border-radius in px
wrapperPadding number 20 Spinner card padding in px
progressValue number 0 Determinate progress 0–100 (static, for progress types)
progressSegments number 20 Segment / dot count for segmented progress types

position values

'center' | 'top' | 'bottom' | 'top-left' | 'top-right'

7.2 PlLoadingTraceOptions — split config

interface PlLoadingTraceOptions {
  shared?: Partial<PlLoadingTraceConfig>; // applied to ALL overlays
  http?:   Partial<PlLoadingTraceConfig>; // HTTP overlays only (overrides shared)
  router?: Partial<PlLoadingTraceConfig>; // router overlays only (overrides shared)
}

7.3 Config resolution order

From lowest to highest priority — later entries win:

DEFAULT_CONFIG
  ← shared
    ← http    (source='http')   ┐
    ← router  (source='router') ┘
  ← [config]  (per-instance input binding, only defined keys)
  ← registry.setConfig / patchConfig  (runtime override — highest priority)

Named overlays (source="none") inherit from shared only.


8. Animation types

8.1 Indeterminate loaders (53)

All are CSS-only, have transparent backgrounds, and respect spinnerOpacity and animationSpeed.

Name Style Best for
spinner Classic rotating ring HTTP requests
dots Three bouncing dots Routing
bar Full-width gradient bar at top Router navigation
ring-fade Segmented ring fading in/out Dashboard
pulse Pulsing expanding circle Manual trace
ripple Two expanding concentric rings Save action
orbit Dot orbiting a center dot Long task
wave Five vertical bars (audio wave) Sync data
bounce-bar Three bars with staggered bounce Inline card
flip Square flipping on 3 axes Admin panel
square-spin Square rotating on multiple axes Developer tool
chase Six dots chasing in a circle Full overlay
dna Two interleaved vertical sine waves Showcase
ellipsis Three dots expanding horizontally Small widgets
hourglass Rotating hourglass shape Processing
infinity Animated figure-eight (∞) Background sync
meteor Comet streaking diagonally Route change
morph Shape-morphing blob Dashboard card
neon-ring Glowing neon spinning ring Dark mode
skeleton Shimmer placeholder bar Content loading
typing Three chat-bubble typing dots Chat / logs
windmill Four-blade windmill Demo
cube-grid 3×3 cubes pulsing in cascade Grid data
fading-circle 12 dots around a clock face Generic loading
double-ring Two concentric rings, opposite directions Modal overlay
radar Radar sweep sector Network trace
jelly Squash-and-stretch blob Friendly UI
rotate-plane Single plane rotating in 3D Admin action
wandering-cubes Two squares orbiting each other Showcase
grid-fade 3×3 grid fading in cascade Tables
three-bounce Three balls bouncing horizontally Button loading
arc SVG arc with animated stroke-dashoffset Default choice
google-line Material-style indeterminate bar Router top bar
route-progress Progressive fill bar Router navigation
matrix-rain Falling code characters Dark / network
terminal-cursor Blinking block cursor Developer UI
cyber-scan Horizontal scan line Network tracing
liquid-orb Liquid blob orbiting Full overlay
elastic-dots Elastically bouncing dots HTTP loading
gradient-ring Ring with conic gradient Modal loading
data-stream Flowing data particles API calls
network-nodes Connected nodes pulsing Network trace
loading-stripes Animated diagonal stripes bar Progress bar
glitch RGB-offset glitch effect Dark showcase
halo-pulse Dot with pulsing halo Small loading
ios-dots iOS-style activity indicator Inline loading
android-bar Material indeterminate bar Top loading
neon-bars Neon equalizer bars Dark UI
rotating-dashes Rotating dashed circle Generic loading
ping-dot Network ping indicator Network ping
progress-pill Animated pill bar Button / card
angular-orbit Angular logo orbital animation Angular library
copilot-border Glowing border that travels the container AI / Chat box

8.2 Determinate progress bars (23)

These types consume a progress value 0–100 and render it visually.
Feed the value via [progress] input, config.progressValue, or registry.setProgress(name, value).

Name Visual Best for
progress-ring SVG circular arc fill File upload
progress-segments Rectangular segments ring Batch operation
progress-dots High-density dot ring Loading stage
progress-bar Horizontal fill bar with glow File upload
progress-striped Animated diagonal stripes fill Long operation
progress-thin 3 px NProgress-style line + glowing dot Page transition
progress-steps Horizontal step circles Wizard / onboarding
progress-blocks Vertical equalizer columns Audio / media
progress-battery Battery shape with charging fill Charge / energy
progress-dial Conic-gradient donut with % label Dashboard metric
progress-level Liquid fill circle (bottom→top) Liquid / fluid
progress-arc SVG half-circle speedometer gauge Speed / score
progress-counter Large % number + thin conic ring KPI / stat card
progress-wave-bar Wavy leading-edge fill bar Liquid / upload
progress-pixel LED 10×2 pixel grid Retro / game UI
progress-clock Conic dial + rotating clock hand Timer / schedule
progress-tower Narrow vertical column fill Storage / capacity
progress-dots-row Horizontal row of dot indicators Step indicator
progress-gradient-ring SVG ring with linear-gradient stroke Profile / avatar
progress-text-fill Giant % text filled bottom-up Score / stat
progress-neon-bar Ultra-glow neon bar (multi-layer bloom) Cyberpunk / game
progress-scan Fill bar with scanline on leading edge Scan / process
progress-split-bar Symmetric fill from center outward Symmetric load

9. Services

9.1 PlLoadingStateService

Inject this to manually trigger the global loading state, or to read the resolved config signals.

import { inject } from '@angular/core';
import { PlLoadingStateService } from 'pl-loading-trace';

@Component({ ... })
export class MyComponent {
  private readonly loadingState = inject(PlLoadingStateService);

  async generateReport() {
    this.loadingState.setHttpLoading(true);
    try {
      await this.reportService.generate();
    } finally {
      this.loadingState.setHttpLoading(false);
    }
  }
}

Members

Member Type Description
config Signal<Required<PlLoadingTraceConfig>> Resolved shared config
httpConfig Signal<Required<PlLoadingTraceConfig>> Resolved HTTP config
routerConfig Signal<Required<PlLoadingTraceConfig>> Resolved router config
loading$ Observable<boolean> Combined HTTP + router stream
httpLoading$ Observable<boolean> HTTP stream only
routerLoading$ Observable<boolean> Router stream only
setHttpLoading(v) void Manually set HTTP loading state
setRouterLoading(v) void Manually set router loading state

9.2 PlLoadingRegistryService

Full imperative control over any named overlay instance.

private readonly registry = inject(PlLoadingRegistryService);

Methods — loading control

Method Signature Description
start (name, label?) => void Start loading; optionally set a dynamic label
stop (name) => void Stop loading and clear the dynamic label
toggle (name, label?) => void Toggle state
isActive (name) => boolean Returns current active state (synchronous)
register (name) => Signal<boolean> Read-only active signal (used by the component)
registerLabel (name) => Signal<string> Read-only label signal

Methods — determinate progress

Method Signature Description
setProgress (name, value: 0–100) => void Set progress value
resetProgress (name) => void Revert to config.progressValue
startProgress (name, label?) => void start() + setProgress(0) combined
completeProgress (name, delayMs? = 300) => void Set 100%, then stop after delay
registerProgress (name) => Signal<number | null> Read-only progress signal

Methods — runtime config override

See §10 for full details.

Method Signature Description
setConfig (name, config) => void Replace config override entirely. label is routed to the label signal
patchConfig (name, config) => void Shallow-merge into existing override. Passing { label: '…' } alone is valid
getConfig (name) => Partial<PlLoadingTraceConfig> Snapshot of current override including active label — use to save before a temporary change
resetConfig (name) => void Remove all overrides and clear the dynamic label (revert to defaults)
registerConfigOverride (name) => Signal<…> Read-only override signal (used by component)

10. Runtime config override API

Every named overlay can have its config overridden at runtime via the registry — independently of the global config and the [config] input binding.

The priority chain is:

Global defaults  ←  [config] input  ←  registry.setConfig / patchConfig

The registry always wins. This means you can change any property — including animationType — without touching the template.

private readonly registry = inject(PlLoadingRegistryService);

// Replace the whole config override (label is routed to the label signal)
registry.setConfig('upload', {
  animationType: 'progress-neon-bar',
  color: '#ef4444',
  size: 80,
  label: 'Uploading…',
});

// Update only specific keys — passing { label } alone is valid
registry.patchConfig('upload', { color: '#10b981' });     // change color only
registry.patchConfig('upload', { label: '2 of 5…' });     // change label only

// Revert to template / global defaults (also clears dynamic label)
registry.resetConfig('upload');

Save and restore

Use getConfig() to snapshot the current state before a temporary change so it can be restored exactly:

// Save current state
const saved = this.registry.getConfig('status-badge');

// Temporary change
this.registry.patchConfig('status-badge', { color: '#ef4444', label: 'Error!' });

// Restore
this.registry.setConfig('status-badge', saved);

getConfig returns a Partial<PlLoadingTraceConfig> that includes the active label key so nothing is lost on restore.

Practical use: change animation on the fly

onUploadStart() {
  this.registry.setConfig('file-task', { animationType: 'progress-bar', color: '#3b82f6' });
  this.registry.startProgress('file-task');
}

onUploadProgress(pct: number) {
  this.registry.setProgress('file-task', pct);
}

onUploadDone() {
  this.registry.patchConfig('file-task', { color: '#22c55e' }); // go green
  this.registry.completeProgress('file-task', 600);
}

onUploadError() {
  this.registry.setConfig('file-task', { animationType: 'spinner', color: '#ef4444' });
  setTimeout(() => this.registry.stop('file-task'), 2000);
}

11. Practical examples

11.1 Full-screen HTTP spinner

<!-- app.component.html -->
<pl-loading-overlay source="http" />
<router-outlet />
providePlLoadingTrace({
  http: {
    animationType: 'spinner',
    color:         '#6c63ff',
    size:          56,
    modal:         true,
    backdropColor: 'rgba(0, 0, 0, 0.6)',
    backdropBlur:  4,
  },
})

11.2 Router navigation bar

<pl-loading-overlay source="router" />
providePlLoadingTrace({
  router: {
    animationType:  'bar',
    modal:          false,
    barColorStart:  '#6c63ff',
    barColorEnd:    '#ff6584',
    barHeight:      3,
  },
})

11.3 HTTP spinner + router bar combined

<pl-loading-overlay source="router" />
<pl-loading-overlay source="http" />
<router-outlet />
providePlLoadingTrace({
  shared: { debounceMs: 150 },
  http: {
    animationType: 'cube-grid',
    color:         '#3b82f6',
    modal:         true,
    backdropColor: 'rgba(15, 23, 42, 0.65)',
  },
  router: {
    animationType:  'bar',
    modal:          false,
    barColorStart:  '#818cf8',
    barColorEnd:    '#f472b6',
    barHeight:      4,
  },
})

11.4 Contained card spinner

The overlay fills the nearest position: relative ancestor instead of the viewport.

<div style="position: relative; width: 300px; height: 200px;">
  <pl-loading-overlay
    source="http"
    [contained]="true"
    [config]="{ animationType: 'ripple', color: '#10b981', size: 48, modal: false }" />

  <h3>Card title</h3>
  <p>Card content loaded via HTTP</p>
</div>

11.5 Named overlay — manual control

Use source="none" so the overlay only responds to registry calls.

<div class="product-card" style="position: relative; min-height: 100px;">
  <pl-loading-overlay
    name="save-product"
    source="none"
    [contained]="true"
    [config]="{ animationType: 'dots', color: '#f59e0b', modal: false }" />

  <h4>Product details</h4>
  <button (click)="save()">Save</button>
</div>
save() {
  this.registry.start('save-product', 'Saving…');
  this.api.saveProduct().subscribe({
    complete: () => this.registry.stop('save-product'),
    error:    () => this.registry.stop('save-product'),
  });
}

11.6 Named overlay on a button

<button style="position: relative; overflow: hidden; min-width: 110px;" (click)="upload()">
  <pl-loading-overlay
    name="upload-btn"
    source="none"
    [contained]="true"
    [config]="{ animationType: 'arc', color: '#fff', size: 18, modal: false, spinnerOpacity: 0.9 }" />
  Upload
</button>
upload() {
  this.registry.start('upload-btn');
  this.fileService.upload(file).pipe(
    finalize(() => this.registry.stop('upload-btn'))
  ).subscribe();
}

11.7 Determinate progress bar

Static value via [config] — useful for declarative templates:

<pl-loading-overlay
  source="none"
  name="install-progress"
  [config]="{
    animationType:  'progress-ring',
    color:          '#3b82f6',
    size:           72,
    modal:          false,
    progressValue:  42
  }" />

Or via the [progress] input (reactive):

<pl-loading-overlay
  source="none"
  name="install-progress"
  [config]="{ animationType: 'progress-ring', color: '#3b82f6', size: 72, modal: false }"
  [progress]="uploadPercent()" />

11.8 Dynamic progress with registry

startUpload() {
  this.registry.startProgress('upload', 'Uploading…');

  this.uploader.progress$.subscribe(pct => {
    this.registry.setProgress('upload', pct);
  });

  this.uploader.complete$.subscribe(() => {
    this.registry.completeProgress('upload', 500); // show 100% for 500ms, then hide
  });
}
<pl-loading-overlay
  name="upload"
  source="none"
  [contained]="true"
  [config]="{ animationType: 'progress-bar', color: '#3b82f6', size: 80, modal: false }" />

11.9 Runtime config change

// Show as indeterminate spinner while connecting
this.registry.setConfig('ws-status', { animationType: 'pulse', color: '#f59e0b', label: 'Connecting…' });
this.registry.start('ws-status');

// Connection established — switch to green progress ring
this.registry.setConfig('ws-status', { animationType: 'progress-ring', color: '#22c55e', label: 'Syncing…' });

// Change only the label without touching any other property
this.registry.patchConfig('ws-status', { label: 'Live' });

// Revert to template defaults (clears label too)
this.registry.resetConfig('ws-status');

Save / restore pattern

// Save state before a temporary error highlight
const saved = this.registry.getConfig('ws-status');
this.registry.patchConfig('ws-status', { color: '#ef4444', label: 'Reconnecting…' });

// After recovery, restore exact prior appearance
this.registry.setConfig('ws-status', saved);

11.10 Label text

<!-- Static label via [config] input -->
<pl-loading-overlay source="http" [config]="{ label: 'Loading data…', labelColor: '#ffffff' }" />
// Dynamic label — set on start, cleared automatically on stop
this.registry.start('my-overlay', 'Uploading file 1 of 3…');

// Change label only at any point (no other properties affected)
this.registry.patchConfig('my-overlay', { label: 'Uploading file 2 of 3…' });

// Label via setConfig / patchConfig has the same priority as start(name, label)
// — both route through the same internal label signal
this.registry.patchConfig('my-overlay', { label: 'Almost done…', color: '#10b981' });

Auto-contrast rules:

  • modal: true → label is white (readable on dark backdrop)
  • modal: false → label matches color (readable on page background)
  • Override with explicit labelColor when needed

11.11 Custom GIF

<pl-loading-overlay
  source="http"
  [config]="{ customGifUrl: '/assets/loading.gif', size: 80 }" />

11.12 Debounce (avoid flicker)

Requests shorter than debounceMs will not show the overlay at all. The overlay hides immediately when loading stops (start-only debounce). Default is 150 ms.

debounceMs can be set globally via shared, or overridden independently per source via the http / router sections:

providePlLoadingTrace({
  shared: { debounceMs: 150 },    // default for both
  http:   { debounceMs: 300 },    // HTTP overlay: only shows for requests > 300 ms
  router: { debounceMs: 0  },     // router bar: appears instantly
})

11.13 Exclude URLs from tracking

providePlLoadingTrace({
  http: {
    excludeUrls: ['/api/health', '/api/ping', 'analytics.google.com'],
  },
})

11.14 Split http / router config

providePlLoadingTrace({
  shared: {
    enableHttpTracer:   true,
    enableRouterTracer: true,
    debounceMs:         200,
  },
  http: {
    animationType: 'fading-circle',
    color:         '#6366f1',
    size:          52,
    modal:         true,
    label:         'Loading…',
  },
  router: {
    animationType:  'bar',
    modal:          false,
    barColorStart:  '#6366f1',
    barColorEnd:    '#ec4899',
    barHeight:      3,
  },
})

11.15 Multiple named cards grid

interface DemoCard {
  name: string;
  type: AnimationType;
  color: string;
  label?: string;
}

@Component({ ... })
export class DashboardComponent {
  readonly registry = inject(PlLoadingRegistryService);

  readonly cards: DemoCard[] = [
    { name: 'card-users',    type: 'spinner',       color: '#3b82f6', label: 'Loading users…' },
    { name: 'card-orders',   type: 'dots',          color: '#f59e0b' },
    { name: 'card-products', type: 'cube-grid',     color: '#10b981' },
    { name: 'card-stats',    type: 'fading-circle', color: '#ec4899' },
  ];

  start(card: DemoCard, ms = 3000) {
    this.registry.start(card.name, card.label);
    setTimeout(() => this.registry.stop(card.name), ms);
  }
}
<div class="cards-grid">
  @for (card of cards; track card.name) {
    <div class="card">
      <pl-loading-overlay
        [name]="card.name"
        source="none"
        [contained]="true"
        [config]="{ animationType: card.type, color: card.color, modal: false }" />

      <h4>{{ card.name }}</h4>
      <button (click)="start(card)">▶ Start</button>
      <button (click)="registry.stop(card.name)">■ Stop</button>
    </div>
  }
</div>
.cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 1rem;
}

.card {
  position: relative; /* required for [contained]="true" */
  min-height: 130px;
  border: 1px solid #e2e8f0;
  border-radius: 14px;
  padding: 1rem;
}

11.16 spinnerOpacity — transparent animation

<!-- Semi-transparent neon ring on a blurred backdrop -->
<pl-loading-overlay
  source="http"
  [config]="{
    animationType:  'neon-ring',
    spinnerOpacity: 0.6,
    modal:          true,
    backdropColor:  'rgba(0, 0, 0, 0.4)'
  }" />

<!-- Ghost shimmer — no backdrop, partially transparent skeleton -->
<pl-loading-overlay
  source="http"
  [contained]="true"
  [config]="{
    animationType:  'skeleton',
    spinnerOpacity: 0.35,
    modal:          false
  }" />

12. Exported symbols

Symbol Kind Description
providePlLoadingTrace function Standalone provider factory
PlLoadingTraceModule NgModule For NgModule apps (forRoot(options))
PlLoadingOverlayComponent Component The overlay component
PlLoadingStateService Injectable Global loading state + config signals
PlLoadingRegistryService Injectable Named overlay control + progress + runtime config
PlLoadingHttpInterceptor Injectable Class-based HTTP interceptor
plLoadingHttpInterceptor HttpInterceptorFn Functional HTTP interceptor
PlRoutingTrackerService Injectable Router navigation tracker
PL_LOADING_TRACE_CONFIG InjectionToken Config injection token
PL_LOADING_TRACE_DEFAULT_CONFIG const Default config values
PlLoadingTraceConfig interface Full config interface
PlLoadingTraceOptions interface Split config interface
AnimationType type Union of all 74 animation names
AnimationSpeed type number (ms)
LoadingPosition type 'center' | 'top' | 'bottom' | 'top-left' | 'top-right'
LoadingSource type 'http' | 'router' | 'both' | 'none'

13. Building & Publishing

Build the library

npx ng build pl-loading-trace

Build artifacts land in dist/pl-loading-trace/.

Publish to npm

cd dist/pl-loading-trace
npm publish

Run the demo app

npm start          # development server → http://localhost:4200
npm run build      # production build
npm run deploy:demo # deploy to GitHub Pages

Run tests

ng test

Made with ❤️ by Luca Piciollo · MIT License

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors