npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rm-graph/angular

v0.1.26

Published

RM-Graphs Angular - Angular components for RM-Graphs charting library

Readme

@rm-graph/angular

RM-Graphs Angular - Angular components for RM-Graphs charting library.

Installation

npm install @rm-graph/angular
# or just the core for direct usage
npm install @rm-graph/core

scichart is automatically installed as a dependency!

Usage Options

Option 1: Using Core Library Directly (Recommended)

The most reliable approach is to use the core library directly in your Angular components:

import { Component, ElementRef, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
import {
  Column3DChart,
  createColumn3DChart,
} from '@rm-graph/core';

@Component({
  selector: 'app-chart',
  standalone: true,
  template: `<div #chartContainer style="width: 100%; height: 400px;"></div>`
})
export class ChartComponent implements AfterViewInit, OnDestroy {
  @ViewChild('chartContainer') chartRef!: ElementRef<HTMLDivElement>;
  private chart: Column3DChart | null = null;

  chartData = [[45, 1500, 1], [90, 2200, 2], [180, 1800, 3]];

  async ngAfterViewInit(): Promise<void> {
    this.chart = await createColumn3DChart(this.chartRef.nativeElement, {
      theme: 'dark',
      data: this.chartData,
      xAxisTitle: 'Phase Angle (°)',
      yAxisTitle: 'Amplitude (mVp)',
      zAxisTitle: 'Cycle',
    });
  }

  ngOnDestroy(): void {
    this.chart?.destroy();
  }

  // Update data dynamically
  updateData(newData: number[][]): void {
    this.chart?.setData(newData);
  }

  // Change theme
  setTheme(theme: string): void {
    this.chart?.setOptions({ theme });
  }
}

Option 2: Using Angular Components (Standalone)

Import the pre-built Angular components:

import { Component } from '@angular/core';
import { Column3DChartComponent, PRPDChartComponent, TrendsChartComponent } from '@rm-graph/angular';

@Component({
  selector: 'app-dashboard',
  standalone: true,
  imports: [Column3DChartComponent, PRPDChartComponent, TrendsChartComponent],
  template: `
    <sui-column3d-chart
      [data]="chartData"
      height="400px"
      theme="dark"
      xAxisTitle="Phase Angle (°)"
      yAxisTitle="Amplitude (mVp)"
      zAxisTitle="Cycle"
      (ready)="onChartReady($event)"
    />
  `
})
export class DashboardComponent {
  chartData = [[45, 1500, 1], [90, 2200, 2], [180, 1800, 3]];

  onChartReady(chart: any) {
    console.log('Chart ready!', chart);
  }
}

Components

<rm-column3d-chart>

| Input | Type | Default | Description | |-------|------|---------|-------------| | data | number[][] | [] | Chart data as [x, y, z] tuples | | width | string | '100%' | Chart width | | height | string | '400px' | Chart height | | theme | string \| ThemeConfig | - | Theme | | xAxisTitle | string | - | X-axis title | | yAxisTitle | string | - | Y-axis title | | zAxisTitle | string | - | Z-axis title |

| Output | Type | Description | |--------|------|-------------| | ready | EventEmitter<Column3DChart> | Emitted when chart is ready | | chartError | EventEmitter<Error> | Emitted on error |

Chart Types Available from Core

  • createColumn3DChart - 3D column charts for phase-resolved data
  • createPRPDChart - Phase Resolved Partial Discharge heatmaps
  • createSurface3DChart - 3D surface visualization

PRPDChart

The PRPDChartComponent displays Phase Resolved Partial Discharge data as an interactive heatmap.

Usage

import { Component } from '@angular/core';
import { PRPDChartComponent } from '@rm-graph/angular';
import type { PRPDDataPoint, PRPDChartStats } from '@rm-graph/core';

@Component({
  selector: 'app-dashboard',
  standalone: true,
  imports: [PRPDChartComponent],
  template: `
    <sui-prpd-chart
      id="my-prpd-chart"
      title="PRPD Analysis"
      [data]="prpdData"
      height="400px"
      [scalingFactor]="1"
      unitOfMeasurement="mVp"
      [maxPeak]="5000"
      [minPeak]="-5000"
      [maxPhaseAngle]="360"
      [showSineWave]="true"
      [showColorPalette]="true"
      [showMaximizeIcon]="true"
      [colorMin]="0"
      [colorMax]="100"
      (statsChange)="onStatsChange($event)"
    />
  `
})
export class DashboardComponent {
  // Data format: [phaseAngle, amplitude, count][]
  prpdData: PRPDDataPoint[] = [
    [45, 1500, 10],   // 45° phase, 1500mV amplitude, 10 pulses
    [90, 2200, 25],   // 90° phase, 2200mV amplitude, 25 pulses
    [180, -1800, 15], // 180° phase, -1800mV amplitude, 15 pulses
    [270, -2500, 30], // 270° phase, -2500mV amplitude, 30 pulses
  ];

  onStatsChange(stats: PRPDChartStats) {
    console.log('Peak:', stats.peakValue, 'Total:', stats.totalCount);
  }
}

PRPDDataPoint Type

type PRPDDataPoint = [number, number, number];
// [phaseAngle, amplitude, count]

| Index | Type | Description | |-------|------|-------------| | 0 | number | Phase angle in degrees (0-360) | | 1 | number | Amplitude value (positive or negative) | | 2 | number | Pulse count at this phase/amplitude |

Inputs

| Input | Type | Default | Description | |-------|------|---------|-------------| | id | string | auto-generated | Unique ID for the chart | | title | string | - | Chart title displayed in header | | data | PRPDDataPoint[] | [] | Array of [phase, amplitude, count] tuples | | height | string | '400px' | Chart height | | width | string | '100%' | Chart width | | scalingFactor | number | 1 | Multiplier for amplitude values | | unitOfMeasurement | string | 'mVp' | Unit label for amplitude axis | | maxPeak | number | 5000 | Maximum amplitude value | | minPeak | number | -5000 | Minimum amplitude value | | maxPhaseAngle | number | 360 | Maximum phase angle | | yAxisRange | number | - | Y-axis visible range | | showSineWave | boolean | true | Show sine wave overlay | | showColorPalette | boolean | true | Show color legend bar | | showMaximizeIcon | boolean | false | Show maximize button | | isMaximized | boolean | false | Current maximized state | | colorMin | number | 0 | Minimum color scale value | | colorMax | number | 100 | Maximum color scale value | | resolutionLabel | PRPDResolutionLabel | - | Resolution mode config | | windowingData | PRPDWindowingRegion[] | - | Windowing filter regions |

Outputs

| Output | Type | Description | |--------|------|-------------| | ready | EventEmitter<PRPDChart> | Emitted when chart is initialized | | statsChange | EventEmitter<PRPDChartStats> | Emitted when statistics update | | resolutionChange | EventEmitter<PRPDResolutionLabel> | Emitted when resolution mode changes | | windowingClick | EventEmitter<void> | Emitted when windowing button clicked | | yAxisRangeChange | EventEmitter<number> | Emitted when Y-range changes | | maximizeToggle | EventEmitter<void> | Emitted when maximize toggled |

Features

  • 🎨 Heatmap Visualization - Color-coded pulse density display
  • 📊 Color Palette Legend - Visual scale for pulse counts
  • 〰️ Sine Wave Overlay - Reference waveform display
  • 🔍 Zoom & Pan - Interactive data exploration
  • 📏 Y-Range Control - Adjustable amplitude range (100-5000 or custom)
  • 🔄 Resolution Modes - UNI/BI and HI/LO toggle options
  • 🪟 Windowing Support - Filter specific phase/amplitude regions
  • 📸 Screenshot - Export chart as PNG image
  • 📈 Statistics - Real-time peak value and total count

TrendsChart

The TrendsChartComponent displays multiple time series with interactive features.

Usage

import { Component } from '@angular/core';
import { TrendsChartComponent, TrendLineData } from '@rm-graph/angular';

@Component({
  selector: 'app-dashboard',
  standalone: true,
  imports: [TrendsChartComponent],
  template: `
    <sui-trends-chart
      id="my-trends-chart"
      [data]="trendsData"
      height="500px"
      [showLegend]="true"
      [showResetButton]="true"
      [showPointMarkerToggle]="true"
      [showOverview]="true"
    />
  `
})
export class DashboardComponent {
  trendsData: TrendLineData[] = [
    {
      name: 'Channel 1',
      values: [150, 145, 160, 155, 148],
      timestamps: [1707012000000, 1707012060000, 1707012120000, 1707012180000, 1707012240000],
      color: '#3b82f6',
      uom: 2,
    },
    {
      name: 'Channel 2',
      values: [100, 95, 110, 105, 98],
      timestamps: [1707012000000, 1707012060000, 1707012120000, 1707012180000, 1707012240000],
      color: '#ef4444',
      uom: 2,
    },
  ];
}

TrendLineData Interface

| Property | Type | Required | Description | |----------|------|----------|-------------| | name | string | ✅ | Display name for the series (shown in legend) | | values | number[] | ✅ | Array of numeric values (Y-axis data points) | | timestamps | number[] | ✅ | Array of timestamps in milliseconds (X-axis data points) | | color | string | ✅ | Line color (hex or CSS color, e.g., #3b82f6) | | uom | number | ❌ | Optional unit of measurement code |

Note: The values and timestamps arrays must have the same length.

Inputs

| Input | Type | Default | Description | |-------|------|---------|-------------| | id | string | auto-generated | Unique ID for the chart | | data | TrendLineData[] | [] | Array of trend line data | | height | string | '400px' | Chart height | | xAxisTitle | string | "Time (HH:MM)" | X-axis title | | yAxisTitle | string | Based on chartType | Y-axis title | | showLegend | boolean | true | Show interactive legend | | showResetButton | boolean | true | Show reset zoom button | | showPointMarkerToggle | boolean | true | Show point marker toggle | | showOverview | boolean | true | Show overview mini-map |

Features

  • 📊 Interactive Legend - Click to toggle series visibility
  • 🔍 Zoom & Pan - Rubber band zoom, mouse wheel zoom, drag to pan
  • 🔄 Reset Zoom - One-click reset to initial view
  • 📍 Point Markers - Toggle visibility of data point markers
  • 🗺️ Overview Chart - Mini-map for navigation
  • 📸 Screenshot - Export chart as PNG with header
  • 📅 Custom Date Labels - Formatted as DD/MM/YYYY HH:MM

Theming

import { getThemeManager } from '@rm-graph/core';

// Built-in themes: 'light', 'dark', 'modern', 'midnight'

// Register custom theme
getThemeManager().registerTheme({
  name: 'custom',
  backgroundColor: '#1a1a2e',
  colorPalette: ['#ff6b6b', '#4ecdc4'],
  // ...
});

🔑 SciChart License Configuration

This package uses SciChart.js, which requires a valid license key to remove watermarks. The license system supports encrypted storage and multiple configuration methods.

Complete License Flow

Step 1: Get Your SciChart License Key

  1. Register your domain at SciChart License Portal
  2. Add allowed domains to your license:
    • For production: yourdomain.com, app.yourdomain.com
    • For development: localhost, 127.0.0.1, or your local IP (e.g., 192.168.140.16)
  3. Copy your license key (format: xxxx-xxxx-xxxx-xxxx)

Important: SciChart licenses are domain-locked. Make sure to register all domains where you'll use the charts.

Step 2: Configure License in Your App

You have three options to configure the license:

Option A: Using LicenseManager Component (Recommended for User-Facing Apps)

Add the LicenseManagerComponent to your settings page:

import { Component } from '@angular/core';
import { LicenseManagerComponent } from '@rm-graph/angular';
import type { LicenseStatus } from '@rm-graph/angular';

@Component({
  selector: 'app-settings',
  standalone: true,
  imports: [LicenseManagerComponent],
  template: `
    <rm-license-manager
      title="SciChart License Configuration"
      placeholder="Paste your SciChart license key here"
      theme="light"
      (licenseChange)="onLicenseChange($event)"
    ></rm-license-manager>
  `
})
export class SettingsComponent {
  onLicenseChange(status: LicenseStatus) {
    if (status.isApplied) {
      console.log('License activated!');
      // Optionally reload to apply to existing charts
      window.location.reload();
    }
  }
}

User Flow:

  1. User opens settings page
  2. User enters SciChart license key in the input field
  3. User clicks "Save License"
  4. License is encrypted and stored in localStorage
  5. License is immediately applied to SciChart
  6. User may need to reload page for existing charts to pick up the license
Option B: Programmatic Setup (For Admin/Backend Configuration)
// In your app initialization (main.ts or app.component.ts)
import { loadLicense, setLicense } from '@rm-graph/angular';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';

// Load existing license on app startup
async function initializeApp() {
  const loaded = await loadLicense();
  if (loaded) {
    console.log('License loaded from storage');
  } else {
    console.log('No license found - charts will show watermark');
  }
  
  // Bootstrap your app
  bootstrapApplication(AppComponent).catch(err => console.error(err));
}

initializeApp();

// To set a license programmatically:
async function configureLicense(licenseKey: string) {
  const success = await setLicense(licenseKey);
  if (success) {
    console.log('License saved and applied!');
  }
}
Option C: Environment Variable (For Build-Time Configuration)
# .env file or environment configuration
SCICHART_LICENSE_KEY=your-license-key-here
// main.ts
import { setupSciChartLicense } from '@rm-graph/angular';

// This will automatically read from SCICHART_LICENSE_KEY
setupSciChartLicense({
  fromEnv: true,
  fromLocalStorage: true, // Also check encrypted storage
});

bootstrapApplication(AppComponent).catch(err => console.error(err));

Step 3: License Storage & Security

The license system provides encrypted storage for security:

  • Encryption: License keys are encrypted using AES-256-GCM (when Web Crypto API is available) or base64 encoding (fallback)
  • Storage: Encrypted license is stored in localStorage under key RMGRAPH_LICENSE_DATA
  • Security Note: While encrypted, this provides obfuscation rather than true security since decryption happens client-side. For production, combine with SciChart's domain-locking.

Storage Flow:

User enters license → Encrypt → Store in localStorage → Decrypt on load → Apply to SciChart

Step 4: Verify License is Working

After configuring the license:

  1. Check browser console - Should see no license errors
  2. Check charts - Should render without "SCICHART" watermark
  3. Check license status:
import { getLicenseStatus, hasStoredLicense, isLicenseApplied } from '@rm-graph/angular';

const status = getLicenseStatus();
console.log('Has license stored:', status.hasLicense);
console.log('License applied:', status.isApplied);

// Or use individual functions
if (hasStoredLicense()) {
  console.log('License exists in storage');
}
if (isLicenseApplied()) {
  console.log('License is active');
}

LicenseManagerComponent API

Selector: <rm-license-manager>

| Input | Type | Default | Description | |-------|------|---------|-------------| | title | string | "SciChart License Configuration" | Title text | | placeholder | string | "Paste your SciChart license key here" | Input placeholder | | successMessageDuration | number | 2000 | Success message display duration (ms) | | autoLoad | boolean | true | Auto-load license on mount | | requireReload | boolean | true | Show reload notice after license change | | theme | 'light' \| 'dark' | 'light' | Component theme | | className | string | - | Custom CSS class name |

| Output | Type | Description | |--------|------|-------------| | licenseChange | EventEmitter<LicenseStatus> | Emitted when license status changes |

License Management Functions

import {
  // Configuration
  configureLicenseEncryption,
  
  // License operations
  setLicense,           // Save and apply license
  loadLicense,          // Load from storage and apply
  removeLicense,        // Remove stored license
  applyLicenseKey,      // Apply directly without storing
  
  // Status checks
  hasStoredLicense,     // Check if license exists in storage
  isLicenseApplied,     // Check if license is active
  getLicenseStatus,     // Get full status object
  validateLicenseFormat, // Validate key format
  
  // Types
  type LicenseStatus,
  type LicenseConfig,
} from '@rm-graph/angular';

Complete Example: Full License Setup

// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { loadLicense } from '@rm-graph/angular';

async function init() {
  await loadLicense();
  bootstrapApplication(AppComponent).catch(err => console.error(err));
}

init();
// app.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LicenseManagerComponent, Column3DChartComponent } from '@rm-graph/angular';
import type { LicenseStatus } from '@rm-graph/angular';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, LicenseManagerComponent, Column3DChartComponent],
  template: `
    <header>
      <h1>My Chart App</h1>
      <button (click)="toggleLicenseManager()">
        🔑 License Settings
      </button>
    </header>

    <rm-license-manager
      *ngIf="showLicenseManager"
      title="SciChart License Configuration"
      placeholder="Paste your SciChart license key here"
      theme="light"
      (licenseChange)="onLicenseChange($event)"
    ></rm-license-manager>

    <rm-column3d-chart
      [data]="chartData"
      height="400px"
    ></rm-column3d-chart>
  `
})
export class AppComponent {
  showLicenseManager = false;
  chartData = [[45, 1500, 1], [90, 2200, 2]];

  toggleLicenseManager() {
    this.showLicenseManager = !this.showLicenseManager;
  }

  onLicenseChange(status: LicenseStatus) {
    console.log('License status:', status);
    // Don't auto-close - let user see success message and click Reload
  }
}

Troubleshooting

Issue: "License key is not valid for this domain"

Cause: Your license key is registered for a different domain than where you're accessing the app.

Solutions:

  1. Check your registered domains in SciChart portal
  2. Add the current domain to your license:
    • For localhost: Add localhost and 127.0.0.1
    • For IP access: Add your IP (e.g., 192.168.140.16)
    • For production: Add your production domain
  3. Access via registered domain: Use the exact domain/IP you registered

Issue: "Failed to save license" / Crypto errors

Cause: Web Crypto API is not available (requires HTTPS or localhost).

Solutions:

  • Use HTTPS: Deploy with SSL certificate
  • Use localhost: Access via http://localhost:PORT instead of IP
  • The system will automatically fallback to base64 encoding (less secure but functional)

Issue: Charts still show watermark after setting license

Solutions:

  1. Reload the page - Existing charts need to be recreated with the license
  2. Check license status: getLicenseStatus() to verify it's applied
  3. Check browser console for license errors
  4. Verify domain matches your registered license

Issue: License works on localhost but not on IP

Cause: SciChart licenses are domain-specific.

Solutions:

  1. Register both domains: Add both localhost AND your IP to the license
  2. Use consistent access method: Always use the same URL format
  3. For development: Consider using localhost only

Security Best Practices

  1. Domain Locking: Always register specific domains in SciChart portal
  2. Environment Variables: Use environment configuration for build-time setup (never commit keys)
  3. HTTPS: Use HTTPS in production for secure crypto operations
  4. User Input: If allowing users to enter licenses, validate format before saving
  5. Storage: Encrypted storage provides obfuscation, not true security (decryption is client-side)

License Status Types

interface LicenseStatus {
  /** Whether a license is stored in localStorage */
  hasLicense: boolean;
  /** Whether the license was successfully applied to SciChart */
  isApplied: boolean;
  /** Error message if license operation failed */
  error?: string;
}

See main README for full documentation.