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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@moxa/graph

v3.0.0-beta.9

Published

- [Install](#install) - [Basic Usage](#basic-usage) - [Components](#components) - [Behaviors](#behaviors) - [Layouts](#layouts) - [Plugins](#plugins) - [Graph Control](#graph-control) - [Event Handling](#event-handling) - [Developer Guidelines](#developer

Readme

Moxa Graph Library

👉 Documentation

Install

Use npm

npm i @moxa/graph

Use pnpm

pnpm add @moxa/graph

Basic Usage

Graph will be generated on the dom element with the specified id

Create a DOM

<div id="container"></div>

Initialize Graph

You need to provide a json config object to initialize graph instance The config include all graph settings, please refer to MxGraphConfig

import { Graph } from '@moxa/graph';

const graph = new Graph({
  container: 'container',
  width: 500,
  height: 500,
  renderer: 'canvas', // 'canvas', 'svg', or 'webgl'
  data: {
    nodes: [
      {
        id: 'node1',
        data: {
          type: 'node-device',
          x: 100,
          y: 50,
        },
      },
      {
        id: 'node2',
        data: {
          type: 'node-device',
          x: 100,
          y: 250,
        },
      },
      {
        id: 'node3',
        data: {
          type: 'node-device',
          x: 300,
          y: 50,
        },
      },
    ],
    edges: [
      {
        id: 'edge1',
        source: 'node1',
        target: 'node2',
        data: {
          type: 'edge-line',
        },
      },
      {
        id: 'edge2',
        source: 'node2',
        target: 'node3',
        data: {
          type: 'edge-quadratic',
        },
      },
    ],
  },
});

// Render the graph
graph.render();

Components

Node Components

  • node-device: Device node with icon and label support
  • node-icon: Icon-only node component
  • node-label: Label-only node component

Edge Components

  • edge-line: Straight line edges
  • edge-polyline: Multi-segment polyline edges
  • edge-quadratic: Curved quadratic edges
  • edge-arrow: Arrow markers for edges
  • edge-label: Labels for edges

Group Components

  • group-device: Device group with expand/collapse functionality

Behaviors

Interactive behaviors for user interaction:

  • brush-select: Rectangle selection
  • click-select: Click to select elements
  • collapse-expand: Group collapse/expand
  • create-edge: Interactive edge creation
  • drag-canvas: Canvas dragging
  • drag-element: Element dragging
  • fix-element-size: Fixed element sizing
  • focus-element: Element focusing
  • hover-activate: Hover activation
  • scroll-canvas: Canvas scrolling
  • select-all: Select all elements
  • zoom-canvas: Canvas zooming

Layouts

Automatic layout algorithms:

  • align: Alignment layout
  • force: Force-directed layout
  • grid: Grid layout
  • ring: Circular ring layout
  • tree: Hierarchical tree layout

Plugins

Extensible plugin system:

  • context-menu: Right-click context menu
  • element-toolbar: Element-specific toolbar
  • fixed-toolbar: Fixed position toolbar
  • graph-background: Customizable graph background
  • history: Undo/redo functionality
  • minimap: Mini navigation map
  • snapline: Element alignment guides
  • tooltip: Element tooltips

Graph Control

You can control the presentation and behavior of the graph by calling the Graph methods

// Data Management
graph.addNode({ id: 'node1', data: { type: 'node-device' } });
graph.updateNode('node1', { data: { x: 100, y: 100 } });
graph.removeNode('node1');

graph.addEdge({ id: 'edge1', source: 'node1', target: 'node2' });
graph.updateEdge('edge1', { data: { type: 'edge-line' } });
graph.removeEdge('edge1');

graph.addGroup({ id: 'group1', data: { type: 'group-device' } });
graph.updateGroup('group1', { data: { collapsed: true } });
graph.removeGroup('group1');

// View Control
graph.zoom(1.5);
graph.zoomTo(2.0, { x: 100, y: 100 });
graph.fitView();
graph.fitCenter();
graph.focusItem('node1');

// Layout
graph.setLayout({ type: 'force', options: {} });
graph.layout();

// Behaviors
graph.setBehavior(['drag-canvas', 'zoom-canvas']);

// Plugins
graph.setPlugin('minimap', { size: [200, 150] });

// Themes
graph.setTheme('dark');
graph.setThemeTokens({ primaryColor: '#1890ff' });

// State Management
graph.setElementState('node1', 'selected', true);
graph.clearElementState('node1', 'selected');

Event Handling

// Listen Events
graph.on('node:click', (event) => {
  console.log('Node clicked:', event.itemId);
});

graph.on('edge:click', (event) => {
  console.log('Edge clicked:', event.itemId);
});

graph.on('canvas:click', (event) => {
  console.log('Canvas clicked:', event.canvas);
});

// Remove event listeners
graph.off('node:click', listener);

// One-time event listeners
graph.once('afterrender', () => {
  console.log('Graph rendered');
});

Developer Guidelines

This section provides guidelines for developers who want to contribute to or extend the Moxa Graph library.

Project Structure & Folder Responsibilities

libs/graph/
├── src/                     # Source code
│   ├── core/                # 🔧 Core functionality (Graph API, models, utilities)
│   ├── components/          # 🎨 Visual components (nodes, edges, groups)
│   ├── behaviors/           # 🖱️ Interactive behaviors (12 behaviors)
│   ├── layouts/             # 📐 Layout algorithms (5 layouts)
│   ├── plugins/             # 🔌 Plugin system (8 plugins)
│   ├── shared/              # 🛠️ Shared utilities and types
│   ├── assets/              # 📦 Static assets (icons, images)
│   ├── styles/              # 🎨 Global styles
│   └── stories/             # 📚 API documentation
├── tests/                   # 🧪 Test utilities
├── e2e/                     # 🎭 End-to-end testing
├── .storybook/              # 📖 Storybook configuration
└── package.json             # 📦 Package configuration

Detailed Folder Responsibilities

Core (src/core/)

Purpose: Essential graph functionality and APIs

  • graph/: Main Graph class implementing the public API, lifecycle management, and core operations
  • model/: TypeScript type definitions, interfaces, and data structures used throughout the library
  • utils/: Core utility functions for graph manipulation, theme management, and element operations
Components (src/components/)

Purpose: Visual rendering components for graph elements

  • Node Components: Different node types (device, icon, label) with specialized rendering logic
  • Edge Components: Various edge types (line, polyline, quadratic) with arrow and label support
  • Group Components: Container components for grouping and organizing elements
  • Shared: Common utilities and transformations used across components
Behaviors (src/behaviors/)

Purpose: User interaction handling and graph manipulation

  • Each behavior handles specific user interactions (clicking, dragging, selecting)
  • Behaviors are modular and can be enabled/disabled independently
  • Includes both mouse and keyboard interaction handling
Layouts (src/layouts/)

Purpose: Automatic positioning algorithms for graph elements

  • Force: Physics-based layout for natural node arrangements
  • Tree: Hierarchical layouts for tree-structured data
  • Grid: Regular grid arrangements for systematic positioning
  • Ring: Circular arrangements for cyclical data
  • Align: Tools for manual element alignment
Plugins (src/plugins/)

Purpose: Extensible features that enhance graph functionality

  • Each plugin is self-contained and optional
  • Plugins can add UI elements (toolbars, tooltips, minimap)
  • Includes data management features (history, context menus)
Shared (src/shared/)

Purpose: Common utilities and infrastructure

  • Types: Shared TypeScript definitions used across modules
  • Utils: Generic utility functions and shared components
  • Transforms: Data transformation pipeline for processing graph configurations
  • Constants: Application-wide constants and configuration values
Testing Infrastructure
  • tests/: Reusable test utilities and helper functions
  • e2e/: End-to-end visual regression testing with Playwright
  • **Each component includes its own test suite with visual snapshots
Documentation & Development
  • .storybook/: Interactive documentation and component playground
  • stories/: Written documentation and API guides
  • **Each component includes comprehensive Storybook stories for demonstration and testing

Development Workflow

  1. Setup Development Environment

    # Clone the repository
    git clone <repository-url>
    
    # Install dependencies
    pnpm install
    
    # Start development server
    pnpm dev
  2. Building the Library

    # Build the library
    pnpm build:graph
    
    # Run unit tests
    pnpm test:graph
    
    # Run e2e tests
    pnpm e2e:graph
    
    # Start Storybook for development
    pnpm storybook:graph

Extending the Library

Creating Custom Components

Custom components are created using the G6 5.x API and registered with specific types:

import { ExtensionController } from '@antv/g6';

// Custom Node Component
const customNode = (context) => {
  const { model, theme } = context;
  const { data } = model;

  return {
    circle: {
      r: 20,
      fill: data.customProperty === 'special' ? 'red' : 'blue',
      stroke: theme.nodeStroke,
      lineWidth: 2,
    },
  };
};

// Register the custom component
ExtensionController.register('node', 'custom-node', customNode);

// Use in graph data
const nodeData = {
  id: 'node1',
  data: {
    type: 'custom-node',
    customProperty: 'special',
  },
};

Creating Custom Layouts

import { ExtensionController } from '@antv/g6';

// Custom Layout Implementation
const customLayout = (graph, options) => {
  return {
    id: 'custom-layout',
    async execute() {
      const { nodes } = graph.getData();
      const { radius = 200 } = options;

      nodes.forEach((node, index) => {
        const angle = (index / nodes.length) * Math.PI * 2;
        node.data.x = Math.cos(angle) * radius;
        node.data.y = Math.sin(angle) * radius;
      });

      return { nodes, edges: graph.getData().edges };
    },
  };
};

// Register the layout
ExtensionController.register('layout', 'custom-layout', customLayout);

// Apply custom layout
graph.setLayout({
  type: 'custom-layout',
  options: { radius: 300 },
});

Creating Plugins

import { BasePlugin } from '@antv/g6';

class CustomPlugin extends BasePlugin {
  constructor(options) {
    super(options);
  }

  init() {
    // Setup plugin
    this.graph.on('node:click', this.handleNodeClick);
  }

  handleNodeClick = (event) => {
    // Implement custom behavior
    console.log('Node clicked:', event.itemId);
  };

  destroy() {
    // Clean up
    this.graph.off('node:click', this.handleNodeClick);
    super.destroy();
  }
}

// Add plugin to graph
graph.setPlugin(
  'custom-plugin',
  new CustomPlugin({
    // plugin options
  }),
);

Development Workflow

This section describes the complete development workflow for contributing to the Moxa Graph library, including component implementation, Storybook documentation, and visual testing.

Directory Structure & Responsibilities

Each component follows a standardized directory structure:

{component-name}/
├── index.ts                    # Component export
├── models/                     # TypeScript type definitions
│   └── index.ts
├── utils/                      # Utility functions
│   └── index.ts
├── stories/                    # Storybook documentation
│   ├── {story-name}.stories.ts     # Storybook configuration
│   ├── {story-name}.component.ts   # Angular wrapper component
│   └── data/                       # Test data definitions
│       └── {story-name}-data.ts
├── tests/                      # Visual regression tests
│   ├── {test-name}.spec.ts
│   └── __snapshots__/          # Visual test snapshots
│       └── {test-name}.png
└── transforms/                 # Data transformation helpers (optional)
    └── index.ts

Directory Responsibilities

  • index.ts: Main component export, registers the component with G6
  • models/: TypeScript interfaces and type definitions for component configuration
  • utils/: Helper functions for styling, positioning, state management
  • stories/: Complete Storybook documentation with interactive examples
    • data/: Reusable test data configurations for different scenarios
  • tests/: Playwright visual regression tests ensuring UI consistency
  • transforms/: Data transformation utilities (for complex components)

Development Process

1. Component Implementation

Start by implementing the core component logic:

// components/my-component/index.ts
import { ExtensionController } from '@antv/g6';

const myComponent = (context) => {
  const { model, theme } = context;
  const { data } = model;

  return {
    // Component rendering logic
    rect: {
      width: data.width || 100,
      height: data.height || 50,
      fill: theme.primaryColor,
    },
  };
};

// Register component
ExtensionController.register('node', 'my-component', myComponent);

export { myComponent };

2. Type Definitions

Define TypeScript interfaces in models/:

// components/my-component/models/index.ts
export interface MyComponentConfig {
  width?: number;
  height?: number;
  customProperty?: string;
}

export interface MyComponentData extends NodeData {
  type: 'my-component';
  config: MyComponentConfig;
}

3. Storybook Documentation

Create comprehensive Storybook stories:

// components/my-component/stories/my-component.stories.ts
import { Meta, StoryObj } from '@storybook/angular';
import { MY_COMPONENT_DATA } from './data/my-component-data';
import { MyComponentComponent } from './my-component.component';

export default {
  title: 'Components/My Component',
  component: MyComponentComponent,
  parameters: {
    docs: {
      description: {
        component: `
# My Component

This component demonstrates custom node rendering capabilities.

## Features
- Configurable dimensions
- Theme integration
- State management

## Usage
\`\`\`typescript
const graph = new Graph({
  container: 'graph',
  data: MY_COMPONENT_DATA,
});
\`\`\`
        `,
      },
    },
  },
  args: {
    graphData: MY_COMPONENT_DATA,
  },
} as Meta;

type Story = StoryObj<MyComponentComponent>;

export const Basic: Story = {};
export const Advanced: Story = {
  args: {
    graphData: ADVANCED_MY_COMPONENT_DATA,
  },
};

4. Angular Wrapper Component

Create an Angular component for Storybook:

// components/my-component/stories/my-component.component.ts
import { Component, input, computed } from '@angular/core';
import { Graph, GraphConfig, GraphData } from '@moxa/graph';
import { StoryWrapperComponent, JsonViewerComponent } from '@shared/utils/components';

@Component({
  selector: 'moxa-vizion-my-component',
  imports: [JsonViewerComponent, StoryWrapperComponent],
  template: `
    <story-wrapper>
      <json-viewer [data]="data()">
        <div container [id]="id"></div>
      </json-viewer>
    </story-wrapper>
  `,
})
export class MyComponentComponent {
  graph!: Graph;
  id: string = Math.random().toString(36).substring(2);

  graphData = input<GraphData>();

  data = computed<GraphConfig>(() => ({
    container: this.id,
    data: this.graphData()!,
  }));

  ngAfterViewInit(): void {
    this.graph = new Graph(this.data());
    this.graph.render();
  }

  ngOnDestroy(): void {
    this.graph.destroy();
  }
}

5. Test Data

Define comprehensive test data:

// components/my-component/stories/data/my-component-data.ts
import { GraphData } from '@moxa/graph';

export const MY_COMPONENT_DATA: GraphData = {
  nodes: [
    {
      id: 'node1',
      data: {
        type: 'my-component',
        x: 100,
        y: 100,
        width: 120,
        height: 60,
      },
    },
    // More test scenarios...
  ],
};

6. Visual Testing

Implement Playwright visual regression tests:

// components/my-component/stories/tests/my-component-basic.spec.ts
import { test } from '@playwright/test';
import { getGraphContainer, getStorybookUrl, verifySnapshot } from '@tests/helpers';

const STORY_ID = 'components-my-component--basic';
const SNAPSHOT_NAME = 'my-component-basic.png';

test.describe('My Component Visual Tests', () => {
  test.beforeEach(async ({ page }) => {
    await page.setViewportSize({ width: 600, height: 500 });
  });

  test('should display basic component correctly', async ({ page, baseURL }) => {
    await page.goto(getStorybookUrl(baseURL, STORY_ID));
    await page.waitForSelector('div.container', { timeout: 10000 });

    const graphContainer = getGraphContainer(page);
    await page.waitForTimeout(1000); // Wait for graph rendering

    await verifySnapshot(graphContainer, SNAPSHOT_NAME);
  });
});

Testing Strategy

Visual Regression Testing

The library uses Playwright for comprehensive visual regression testing:

  1. Snapshot Generation: First test run generates baseline snapshots
  2. Automatic Comparison: Subsequent runs compare against baselines
  3. Pixel-Perfect Accuracy: Detects even minor visual changes
  4. Cross-Browser Support: Ensures consistency across different browsers

Test Configuration

Playwright is configured for optimal visual testing:

// playwright.config.ts highlights
export default defineConfig({
  testMatch: ['**/e2e/**/*.spec.ts', '**/src/**/tests/*.spec.ts'],
  snapshotPathTemplate: `{testFileDir}/__snapshots__/{arg}{ext}`,
  use: {
    viewport: { width: 1280, height: 720 },
    deviceScaleFactor: 1, // Fixed DPR for consistency
    // Disable animations for consistent screenshots
    launchOptions: {
      args: ['--disable-gpu', '--disable-web-security'],
    },
  },
  expect: {
    toMatchSnapshot: {
      maxDiffPixelRatio: 0.01,
      threshold: 0.1,
    },
  },
});

Development Commands

# Start Storybook for development
pnpm storybook:graph

# Run all tests
pnpm test:graph

# Run visual regression tests
pnpm e2e:graph

# Update visual snapshots (when intentional changes are made)
pnpm e2e:graph --update-snapshots

# Build the library
pnpm build:graph

# Generate documentation
pnpm docs:graph

Quality Assurance

Pre-commit Checklist

Before submitting code, ensure:

  1. Component Registration: Component is properly registered with G6
  2. Type Safety: All TypeScript interfaces are defined
  3. Storybook Stories: Complete documentation with examples
  4. Test Data: Comprehensive test scenarios
  5. Visual Tests: All visual regression tests pass
  6. Documentation: Component behavior is clearly documented
  7. Performance: Component renders efficiently for large datasets

Code Review Process

  1. Functionality Review: Verify component logic and API design
  2. Documentation Review: Ensure Storybook stories are comprehensive
  3. Visual Review: Check all visual test snapshots for accuracy
  4. Performance Review: Validate rendering performance
  5. Integration Review: Confirm component works with existing ecosystem

Best Practices

  1. Performance Considerations

    • Use throttling and debouncing for event handlers on large graphs
    • Implement pagination or virtualization for very large datasets
    • Consider using WebGL renderer for graphs with thousands of elements
  2. Accessibility

    • Ensure proper contrast ratios in your themes
    • Add ARIA attributes to important interactive elements
    • Support keyboard navigation for critical operations
  3. Testing

    • Write comprehensive visual regression tests for all component states
    • Create multiple test scenarios covering edge cases
    • Maintain up-to-date snapshots when making intentional visual changes
    • Test across different browsers and device sizes
  4. Documentation

    • Provide clear usage examples in Storybook
    • Document all configuration options
    • Include interactive demos showing component capabilities
    • Explain integration patterns with other components

Troubleshooting

Common issues and their solutions:

  1. Graph not rendering properly

    • Check if container has proper dimensions
    • Verify data format follows required schema
    • Check browser console for errors
  2. Performance issues with large graphs

    • Try different renderers (SVG vs Canvas vs WebGL)
    • Simplify node/edge renderers
    • Use appropriate layout algorithms
  3. Events not firing

    • Verify event listeners are properly attached
    • Check if event propagation is being stopped
    • Ensure targets exist when attaching listeners