quixotic-gol
v0.1.30
Published
A powerful React library for custom Graphviz layout rendering with Web Worker support for handling large graphs without blocking the UI.
Maintainers
Readme
quixotic-gol
A powerful React library for custom Graphviz layout rendering with Web Worker support for handling large graphs without blocking the UI.
Features
- 🚀 Web Worker Support - Process large graphs in background without blocking the UI
- ⚛️ React Components - Ready-to-use React hooks and components
- 🎨 Custom Rendering - Full control over graph visualization with D3.js
- 📦 TypeScript First - Full TypeScript support with comprehensive type definitions
- 🔧 Configurable - Customizable layout configuration (spacing, corner radius, orthogonal rendering)
- 📊 Depth Filtering - Filter graph nodes by depth level
Installation
npm install quixotic-gol
# or
pnpm add quixotic-gol
# or
yarn add quixotic-golPeer Dependencies
npm install react react-domQuick Start
Simplest Way: Using GraphvizRenderer Component (Recommended)
The easiest way to render DOT strings with built-in worker and loading indicator:
import { GraphvizRenderer } from 'quixotic-gol';
function MyGraph() {
const dotString = `
digraph G {
A -> B;
B -> C;
}
`;
return (
<GraphvizRenderer
dotString={dotString}
onSuccess={(jsonString) => {
const json = JSON.parse(jsonString);
console.log('Rendered:', json);
// Use the JSON result for custom rendering
}}
onError={(error) => {
console.error('Error:', error);
}}
/>
);
}Features:
- ✅ Only uses Web Worker (no main thread blocking)
- ✅ Built-in loading indicator with cancel button
- ✅ Automatic error handling
- ✅ Simple, declarative API
See GraphvizRenderer documentation for more examples.
Basic Usage with Hook
import { useGraphvizWorker, renderCustomGraph } from 'quixotic-gol';
import { useRef, useEffect } from 'react';
function MyGraph() {
const containerRef = useRef<HTMLDivElement>(null);
const { layout, isLoading } = useGraphvizWorker();
useEffect(() => {
const dotString = `
digraph G {
A -> B;
B -> C;
}
`;
layout(dotString).then((jsonStr) => {
const json = JSON.parse(jsonStr);
if (containerRef.current) {
renderCustomGraph(
containerRef.current,
json,
{}, // node mapping
null, // max depth
true, // include lesser depths
createLayoutConfig(),
true, // use orthogonal rendering
14 // font size
);
}
});
}, [layout]);
return (
<div>
{isLoading && <p>Loading...</p>}
<div ref={containerRef} />
</div>
);
}With Loading Indicator Component
import {
useGraphvizWorker,
WorkerLoadingIndicator
} from 'quixotic-gol';
function MyGraph() {
const { layout, cancel, isLoading } = useGraphvizWorker();
return (
<div className="relative">
<div ref={containerRef} className="graph-container" />
{isLoading && (
<WorkerLoadingIndicator onCancel={cancel} />
)}
</div>
);
}API Reference
Hooks
useGraphvizWorker()
React hook for processing DOT strings using a Web Worker.
Returns:
{
layout: (dot: string) => Promise<string>; // Process DOT string, returns JSON
cancel: () => void; // Cancel current processing
isLoading: boolean; // Loading state
error: string | null; // Error message
}Components
GraphvizRenderer (Recommended)
A powerful component that renders DOT strings using Web Worker with built-in loading indicator, zoom controls, and edge selection support.
Props:
interface GraphvizRendererProps {
/** DOT string to render (required) */
dotString: string;
/** Node mapping for custom rendering */
nodeMapping?: Record<string, NodeMapping>;
/** SVG font size (default: 14) */
svgFontSize?: number;
/** Custom loading indicator component */
loadingIndicator?: React.ReactNode;
/** Callback when rendering succeeds with parsed JSON result */
onSuccess?: (json: GraphvizJson) => void;
/** Callback when rendering fails */
onError?: (error: Error) => void;
/** Callback on zoom with font size information */
onZoom?: (info: { baseFontSize: number; actualFontSize: number; scale: number }) => void;
/** Callback for render progress with elapsed time in milliseconds */
onRenderProgress?: (elapsedMs: number) => void;
/** Additional class name for the container */
className?: string;
/** Show loading indicator (default: true) */
showLoading?: boolean;
/** Enable debug visual indicators - yellow border and center dot (default: false) */
debug?: boolean;
/** Currently selected edge ID (controlled component) */
selectedEdgeId?: number | null;
/** Callback when an edge is selected or deselected */
onEdgeSelect?: (edgeId: number | null) => void;
/** Use native Graphviz rendering instead of custom rendering (default: false) */
useNativeRendering?: boolean;
/** Custom node width in points (default: 240) */
nodeWidth?: number;
/** Custom node height in points (default: 64) */
nodeHeight?: number;
/** Custom spacing between node columns in points (default: 800) */
nodeColumnSpacing?: number;
}Ref API:
Access programmatic zoom and rendering controls via ref:
interface GraphvizRendererRef {
/** Zoom in by increasing the effective font size */
zoomIn: (percentage?: number) => void;
/** Zoom out by decreasing the effective font size */
zoomOut: (percentage?: number) => void;
/** Toggle between full overview and centered view with normal font size (16px) */
toggleZoom: () => void;
/** Stop the current rendering operation and terminate the worker */
stopRendering: () => void;
}Basic Example:
<GraphvizRenderer
dotString={myDotString}
onSuccess={(json) => console.log('Rendered:', json)}
onError={(err) => console.error(err)}
/>Full-Featured Example with Ref API:
import { useRef } from 'react';
import { GraphvizRenderer, GraphvizRendererRef } from 'quixotic-gol';
function MyGraph() {
const rendererRef = useRef<GraphvizRendererRef>(null);
const [selectedEdge, setSelectedEdge] = useState<number | null>(null);
return (
<div>
{/* Zoom controls */}
<button onClick={() => rendererRef.current?.zoomIn(10)}>Zoom In</button>
<button onClick={() => rendererRef.current?.zoomOut(10)}>Zoom Out</button>
<button onClick={() => rendererRef.current?.toggleZoom()}>Toggle Zoom</button>
<button onClick={() => rendererRef.current?.stopRendering()}>Stop</button>
<GraphvizRenderer
ref={rendererRef}
dotString={dotString}
nodeMapping={nodeMapping}
svgFontSize={14}
className="w-full h-full"
debug={false}
nodeWidth={240}
nodeHeight={64}
nodeColumnSpacing={800}
selectedEdgeId={selectedEdge}
onEdgeSelect={setSelectedEdge}
onZoom={({ actualFontSize, scale }) => {
console.log(`Zoom: ${scale}x, Font: ${actualFontSize}px`);
}}
onRenderProgress={(elapsedMs) => {
console.log(`Rendering: ${elapsedMs}ms`);
}}
onSuccess={(json) => console.log('Rendered:', json)}
onError={(err) => console.error('Error:', err)}
/>
</div>
);
}See GraphvizRenderer documentation for more examples.
WorkerLoadingIndicator
Loading indicator component with cancel button.
Props:
{
onCancel: () => void; // Callback when cancel is clicked
}Functions
renderCustomGraph()
Render a graph with custom styling.
function renderCustomGraph(
container: HTMLDivElement,
json: GraphvizJson,
nodeMapping: Record<string, NodeMapping>,
maxDepth: number | null,
includeLesserDepths: boolean,
config: LayoutConfig,
useOrthogonalRendering: boolean,
svgFontSize: number
): voidsetupZoomPan()
Setup zoom and pan interactions for the graph.
function setupZoomPan(container: HTMLDivElement): voidcreateLayoutConfig()
Create layout configuration with optional overrides.
function createLayoutConfig(overrides?: Partial<LayoutConfig>): LayoutConfigDefault Configuration:
{
edgeSpacing: 80, // Spacing between edges in pixels
cornerRadius: 15, // Corner radius for edge curves
}Types
export interface NodeMapping {
sourceNodeId: string;
targetNodeId: string;
}
export interface GraphvizJson {
objects?: Array<{
_ldraw_?: Array<{ size?: number }>;
// ... other properties
}>;
edges?: Array<any>;
// ... other properties
}
export interface LayoutConfig {
edgeSpacing: number;
cornerRadius: number;
}Configuration Options
Layout Configuration
import { createLayoutConfig } from 'quixotic-gol';
const config = createLayoutConfig({
edgeSpacing: 100, // Increase spacing between parallel edges
cornerRadius: 20, // Smoother corners
});Depth Filtering
// Show only nodes at depth 0-2
renderCustomGraph(
container,
json,
nodeMapping,
2, // maxDepth
true, // includeLesserDepths - show depth 0, 1, and 2
config,
true,
14
);Advanced Usage
Direct Worker Access
For advanced use cases, you can import worker types:
import type { WorkerRequest, WorkerResponse } from 'quixotic-gol/worker';Demo Application
The package includes a full Next.js demo application. To run it:
git clone <repository-url>
cd graphviz-layouter
pnpm install
pnpm devVisit http://localhost:3000 to see the demo.
Building from Source
# Install dependencies
pnpm install
# Build library
pnpm run build:lib
# Build Next.js demo
pnpm run build:next
# Build both
pnpm run buildLicense
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
