Smart Canvas Getting Started Guide

Step-by-step guide to implementing Smart Canvas in your Angular applications

Smart Canvas Getting Started Guide

๐ŸŽฏ Quick Implementation

Step 1: Import the Component

import { SmartCanvasComponent } from '@smart-canvas';

@Component({
  standalone: true,
  imports: [SmartCanvasComponent],
  template: `
    <lib-smart-canvas
      [nodes]="nodes"
      [connections]="connections"
      [config]="canvasConfig"
      (nodeUpdate)="onNodeUpdate($event)"
      (connectionCreate)="onConnectionCreate($event)">
    </lib-smart-canvas>
  `
})
export class MyCanvasComponent {
  // Implementation here
}

Step 2: Define Your Data

import { CanvasNode, CanvasConnection, SmartCanvasConfig } from '@smart-canvas';

export class MyCanvasComponent {
  // Define nodes with ports for connections
  nodes: CanvasNode[] = [
    {
      id: 'start',
      type: 'process',
      position: { x: 100, y: 100, z: 1 },
      size: { width: 120, height: 80 },
      data: { 
        title: 'Start Process',
        description: 'Beginning of workflow'
      },
      appearance: {
        backgroundColor: '#e3f2fd',
        border: { width: 2, style: 'solid', color: '#2196f3' },
        borderRadius: 8
      },
      ports: [
        { id: 'output', type: 'output', position: { x: 1, y: 0.5 } }
      ]
    },
    {
      id: 'process',
      type: 'action',
      position: { x: 300, y: 100, z: 1 },
      size: { width: 120, height: 80 },
      data: { 
        title: 'Process Data',
        description: 'Main processing step'
      },
      appearance: {
        backgroundColor: '#e8f5e8',
        border: { width: 2, style: 'solid', color: '#4caf50' },
        borderRadius: 8
      },
      ports: [
        { id: 'input', type: 'input', position: { x: 0, y: 0.5 } },
        { id: 'output', type: 'output', position: { x: 1, y: 0.5 } }
      ]
    }
  ];

  // Define connections between nodes
  connections: CanvasConnection[] = [
    {
      id: 'conn-1',
      type: 'flow',
      source: { nodeId: 'start', portId: 'output' },
      target: { nodeId: 'process', portId: 'input' },
      appearance: {
        color: '#666',
        width: 2,
        curve: { type: 'bezier', tension: 0.4 },
        markers: { end: { type: 'arrow', size: 8 } }
      }
    }
  ];

  // Configure canvas behavior
  canvasConfig: Partial<SmartCanvasConfig> = {
    features: {
      enablePanning: true,
      enableZooming: true,
      enableSelection: true,
      enableMultiSelection: true,
      enableConnectionCreation: true,
      showGrid: true,
      showToolbar: true
    },
    appearance: {
      theme: 'modern',
      grid: {
        enabled: true,
        type: 'dots',
        size: 40,
        color: '#e5e5e5',
        opacity: 0.4
      }
    },
    interactions: {
      panSensitivity: 1,
      zoomSensitivity: 0.8,
      selectionRectangle: true
    }
  };
}

Step 3: Handle Events

export class MyCanvasComponent {
  // Handle node updates (position, size, data changes)
  onNodeUpdate(event: { nodeId: string; updates: Partial<CanvasNode> }) {
    console.log('Node updated:', event);
    // Update your data model
    const nodeIndex = this.nodes.findIndex(n => n.id === event.nodeId);
    if (nodeIndex >= 0) {
      this.nodes[nodeIndex] = { ...this.nodes[nodeIndex], ...event.updates };
    }
  }

  // Handle new connections created by user
  onConnectionCreate(event: { connection: CanvasConnection }) {
    console.log('Connection created:', event);
    // Add to your connections array
    this.connections = [...this.connections, event.connection];
  }

  // Handle context menu events
  onContextMenu(event: CanvasContextMenuEvent) {
    console.log('Context menu:', event);
    // Show your context menu
  }

  // Handle selection changes
  onSelectionChange(event: { nodeIds: string[]; connectionIds: string[] }) {
    console.log('Selection changed:', event);
    // Update UI based on selection
  }
}

๐ŸŽจ Styling and Themes

Using Built-in Themes

// Modern theme (default)
canvasConfig.appearance.theme = 'modern';

// Space theme (dark with cosmic colors)
canvasConfig.appearance.theme = 'space';

// Professional theme (business-friendly)
canvasConfig.appearance.theme = 'professional';

// Editor theme (code editor inspired)
canvasConfig.appearance.theme = 'editor';

Custom Styling

// Custom node appearance
const customNode: CanvasNode = {
  id: 'custom',
  type: 'custom',
  position: { x: 0, y: 0, z: 1 },
  size: { width: 150, height: 100 },
  appearance: {
    backgroundColor: '#ff6b6b',
    border: { width: 3, style: 'dashed', color: '#ee5a52' },
    borderRadius: 15,
    boxShadow: '0 4px 12px rgba(255, 107, 107, 0.3)'
  },
  data: { title: 'Custom Node' }
};

// Custom connection styling
const customConnection: CanvasConnection = {
  id: 'custom-conn',
  type: 'custom',
  source: { nodeId: 'node1', portId: 'out' },
  target: { nodeId: 'node2', portId: 'in' },
  appearance: {
    color: '#ff6b6b',
    width: 4,
    curve: { type: 'bezier', tension: 0.6 },
    markers: { 
      end: { type: 'arrow', size: 12, color: '#ee5a52' },
      start: { type: 'circle', size: 6, color: '#ff6b6b' }
    },
    strokeDasharray: '5,5' // Dashed line
  }
};

Grid Customization

canvasConfig.appearance.grid = {
  enabled: true,
  type: 'lines', // 'dots', 'lines', 'squares'
  size: 50, // Grid spacing
  color: '#e0e0e0', // Grid color
  opacity: 0.5, // Grid opacity
  strokeWidth: 1, // Line thickness (for lines/squares)
  snapToGrid: true, // Enable node snapping
  snapThreshold: 10 // Snap distance
};

๐Ÿ–ฑ๏ธ User Interactions

Selection Modes

// Single selection only
canvasConfig.features.enableMultiSelection = false;

// Multi-selection with Ctrl/Cmd+Click
canvasConfig.features.enableMultiSelection = true;

// Selection rectangle (drag empty space)
canvasConfig.interactions.selectionRectangle = true;

Pan and Zoom Configuration

canvasConfig.interactions = {
  // Pan settings
  panSensitivity: 1.0, // Pan speed multiplier
  panButton: 'left', // 'left', 'middle', 'right'
  panWithSpace: false, // Require space key for panning

  // Zoom settings
  minZoom: 0.1, // Minimum zoom level
  maxZoom: 5.0, // Maximum zoom level
  zoomStep: 0.15, // Zoom increment
  zoomSensitivity: 0.8, // Mouse wheel sensitivity
  zoomToPoint: true, // Zoom to mouse position

  // Touch gestures
  touchGestures: {
    enabled: true,
    pinch: { enabled: true, minScale: 0.1, maxScale: 5 },
    pan: { enabled: true, minDistance: 10 }
  }
};

Connection Creation

// Enable connection creation
canvasConfig.features.enableConnectionCreation = true;

// Handle connection attempts
onConnectionAttempt(event: ConnectionAttemptEvent) {
  // Validate connection rules
  if (this.canConnect(event.source, event.target)) {
    return true; // Allow connection
  }
  return false; // Reject connection
}

// Custom connection validation
canConnect(source: PortInfo, target: PortInfo): boolean {
  // Example: Only allow output -> input connections
  return source.type === 'output' && target.type === 'input';
}

๐Ÿ“ฑ Mobile Optimization

Touch-Friendly Configuration

canvasConfig.features.enableMobileOptimization = true;

canvasConfig.interactions.touchGestures = {
  enabled: true,
  
  // Tap gestures
  tap: {
    enabled: true,
    maxDistance: 15, // Larger touch target
    maxDuration: 300
  },
  
  // Long press for context menu
  longPress: {
    enabled: true,
    duration: 500,
    hapticFeedback: true
  },
  
  // Pinch to zoom
  pinch: {
    enabled: true,
    minScale: 0.1,
    maxScale: 5,
    momentum: true
  },
  
  // Two-finger pan
  pan: {
    enabled: true,
    multiFingerEnabled: true,
    maxFingers: 2
  }
};

Responsive Node Sizing

// Responsive node sizes based on screen size
const getResponsiveNodeSize = (baseWidth: number, baseHeight: number) => {
  const isMobile = window.innerWidth < 768;
  const scale = isMobile ? 1.2 : 1.0; // 20% larger on mobile
  
  return {
    width: baseWidth * scale,
    height: baseHeight * scale
  };
};

// Apply to nodes
nodes.forEach(node => {
  node.size = getResponsiveNodeSize(node.size.width, node.size.height);
});

๐Ÿ”ง Advanced Features

Performance Optimization

canvasConfig.performance = {
  // Virtual scrolling for large datasets
  enableVirtualScrolling: true,
  maxVisibleNodes: 1000,
  
  // Rendering optimization
  renderBounds: true, // Only render visible elements
  debounceMs: 16, // Debounce updates (60fps)
  
  // Connection optimization
  enableConnectionPooling: true,
  maxConnectionPoolSize: 500,
  
  // Update strategy
  updateStrategy: 'frame-aligned', // Align with requestAnimationFrame
  batchSize: 50 // Batch operations
};

Keyboard Shortcuts

canvasConfig.features.enableKeyboardShortcuts = true;

// Handle custom keyboard shortcuts
onKeyboardShortcut(event: KeyboardEvent) {
  switch (event.key) {
    case 'Delete':
      this.deleteSelectedNodes();
      break;
    case 'd':
      if (event.ctrlKey) {
        this.duplicateSelectedNodes();
      }
      break;
    case 'a':
      if (event.ctrlKey) {
        this.selectAllNodes();
      }
      break;
  }
}

Undo/Redo System

// Enable undo/redo
canvasConfig.features.enableUndo = true;

// Configure history
canvasConfig.history = {
  maxOperations: 50, // Maximum undo steps
  autoSave: true, // Auto-save operations
  debounceMs: 1000 // Group rapid changes
};

// Manual undo/redo
onUndo() {
  this.canvasService.undo();
}

onRedo() {
  this.canvasService.redo();
}

๐ŸŽ›๏ธ Configuration Panel

Enable Live Configuration

// Show configuration panel
canvasConfig.features.showConfigPanel = true;

// Handle configuration changes
onConfigChange(newConfig: Partial<SmartCanvasConfig>) {
  this.canvasConfig = { ...this.canvasConfig, ...newConfig };
}

Custom Configuration Options

// Add custom configuration sections
const customConfigPanel = {
  sections: [
    {
      title: 'Workflow Settings',
      options: [
        {
          key: 'autoSave',
          type: 'boolean',
          label: 'Auto-save workflow',
          value: true
        },
        {
          key: 'validationMode',
          type: 'select',
          label: 'Validation mode',
          options: ['strict', 'permissive', 'off'],
          value: 'strict'
        }
      ]
    }
  ]
};

๐Ÿงช Testing Your Implementation

Unit Tests

describe('MyCanvasComponent', () => {
  let component: MyCanvasComponent;
  let fixture: ComponentFixture<MyCanvasComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [SmartCanvasComponent, MyCanvasComponent]
    });
    fixture = TestBed.createComponent(MyCanvasComponent);
    component = fixture.componentInstance;
  });

  it('should create nodes', () => {
    expect(component.nodes.length).toBeGreaterThan(0);
  });

  it('should handle node selection', () => {
    const nodeId = 'test-node';
    component.onNodeSelect(nodeId);
    expect(component.selectedNodes).toContain(nodeId);
  });
});

Integration Tests

describe('Canvas Interactions', () => {
  it('should create connections between nodes', async () => {
    const canvas = await screen.findByRole('application');
    const sourcePort = await screen.findByTestId('node-1-output');
    const targetPort = await screen.findByTestId('node-2-input');
    
    // Simulate port click to start connection
    fireEvent.click(sourcePort);
    fireEvent.click(targetPort);
    
    // Verify connection was created
    expect(component.connections).toHaveLength(1);
  });
});

๐Ÿš€ Next Steps

  1. Explore Examples: Check out the demo implementation
  2. Read API Docs: Detailed API reference
  3. Migration Guide: Migrate from existing canvas implementations
  4. Advanced Features: Learn about performance optimization

You're now ready to build powerful, interactive canvas applications with Smart Canvas! ๐ŸŽ‰