@utisha/graph-editor 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  import { EventEmitter, OnChanges, OnInit, SimpleChanges } from '@angular/core';
2
2
  import { Graph, GraphEdge, GraphNode, Position } from './graph.model';
3
- import { ContextMenuEvent, GraphEditorConfig, SelectionState, ValidationResult } from './graph-editor.config';
3
+ import { ContextMenuEvent, GraphEditorConfig, NodeTypeDefinition, SelectionState, ValidationResult } from './graph-editor.config';
4
+ import { SvgIconDefinition } from './icons/workflow-icons';
4
5
  import * as i0 from "@angular/core";
5
6
  /**
6
7
  * Main graph editor component.
@@ -34,6 +35,7 @@ export declare class GraphEditorComponent implements OnInit, OnChanges {
34
35
  canvasClick: EventEmitter<Position>;
35
36
  contextMenu: EventEmitter<ContextMenuEvent>;
36
37
  private readonly canvasSvgRef;
38
+ private readonly historyService;
37
39
  internalGraph: import("@angular/core").WritableSignal<Graph>;
38
40
  selection: import("@angular/core").WritableSignal<SelectionState>;
39
41
  validationResult: import("@angular/core").WritableSignal<ValidationResult | null>;
@@ -42,6 +44,8 @@ export declare class GraphEditorComponent implements OnInit, OnChanges {
42
44
  scale: import("@angular/core").WritableSignal<number>;
43
45
  private draggedNode;
44
46
  private dragOffset;
47
+ private draggedNodeOffsets;
48
+ private didDrag;
45
49
  private isPanning;
46
50
  private lastMousePos;
47
51
  private draggedEdge;
@@ -57,6 +61,14 @@ export declare class GraphEditorComponent implements OnInit, OnChanges {
57
61
  source: Position;
58
62
  target: Position;
59
63
  } | null>;
64
+ private isBoxSelecting;
65
+ private boxSelectStart;
66
+ selectionBox: import("@angular/core").WritableSignal<{
67
+ x: number;
68
+ y: number;
69
+ width: number;
70
+ height: number;
71
+ } | null>;
60
72
  transform: import("@angular/core").Signal<string>;
61
73
  gridBounds: import("@angular/core").Signal<{
62
74
  x: number;
@@ -79,6 +91,10 @@ export declare class GraphEditorComponent implements OnInit, OnChanges {
79
91
  updateNode(nodeId: string, updates: Partial<GraphNode>): void;
80
92
  selectNode(nodeId: string | null): void;
81
93
  selectEdge(edgeId: string | null): void;
94
+ /** Toggle a node in/out of the current selection (for Ctrl+Click) */
95
+ toggleNodeSelection(nodeId: string): void;
96
+ /** Toggle an edge in/out of the current selection (for Ctrl+Click) */
97
+ toggleEdgeSelection(edgeId: string): void;
82
98
  onKeyDown(event: KeyboardEvent): void;
83
99
  switchTool(tool: 'hand' | 'line'): void;
84
100
  /** @deprecated Use switchTool('line') instead */
@@ -101,6 +117,16 @@ export declare class GraphEditorComponent implements OnInit, OnChanges {
101
117
  onWheel(event: WheelEvent): void;
102
118
  onContextMenu(event: MouseEvent): void;
103
119
  private emitGraphChange;
120
+ /** Undo the last action (Ctrl+Z) */
121
+ undo(): boolean;
122
+ /** Redo the last undone action (Ctrl+Y / Ctrl+Shift+Z) */
123
+ redo(): boolean;
124
+ /** Check if undo is available */
125
+ canUndo(): boolean;
126
+ /** Check if redo is available */
127
+ canRedo(): boolean;
128
+ /** Clear history and reset to current state */
129
+ clearHistory(): void;
104
130
  private generateId;
105
131
  private recalculateEdgePorts;
106
132
  getNodeSize(node: GraphNode): {
@@ -115,6 +141,38 @@ export declare class GraphEditorComponent implements OnInit, OnChanges {
115
141
  getEdgeSourcePoint(edge: GraphEdge): Position;
116
142
  getEdgeTargetPoint(edge: GraphEdge): Position;
117
143
  getNodeTypeIcon(node: GraphNode): string;
144
+ /**
145
+ * Get image URL for a node icon.
146
+ * Priority: node.data['imageUrl'] > nodeType.iconSvg (converted to data URL) > nodeType.defaultData['imageUrl']
147
+ * Returns null if no image is configured (will render text icon instead).
148
+ */
149
+ getNodeImage(node: GraphNode): string | null;
150
+ /**
151
+ * Convert an SvgIconDefinition to a data URL for use in <image> elements.
152
+ * Caches results to avoid repeated conversion.
153
+ */
154
+ private svgIconCache;
155
+ private svgIconToDataUrl;
156
+ /**
157
+ * Get the SVG icon definition for a node type (for palette rendering).
158
+ * Returns null if no iconSvg is configured.
159
+ */
160
+ getNodeTypeSvgIcon(nodeType: NodeTypeDefinition): SvgIconDefinition | null;
161
+ /**
162
+ * Split SVG path data by newlines for template iteration.
163
+ * Used to render multiple path elements from a single path string.
164
+ */
165
+ splitIconPaths(pathData: string): string[];
166
+ /**
167
+ * Get the position for the node image (top-left corner of image).
168
+ * Uses same positioning logic as icon but accounts for image dimensions.
169
+ */
170
+ getImagePosition(node: GraphNode): Position;
171
+ /**
172
+ * Get the size (width/height) for node images.
173
+ * Images are rendered as squares, sized proportionally to node height.
174
+ */
175
+ getImageSize(node: GraphNode): number;
118
176
  getIconPosition(node: GraphNode): Position;
119
177
  getLabelPosition(node: GraphNode): Position;
120
178
  private findNodeAtPosition;
@@ -1,5 +1,6 @@
1
1
  import { Type } from '@angular/core';
2
2
  import { EdgeStyle, Graph, Position } from './graph.model';
3
+ import { SvgIconDefinition } from './icons/workflow-icons';
3
4
  export interface GraphEditorConfig {
4
5
  /** Node type definitions */
5
6
  nodes: NodesConfig;
@@ -39,8 +40,15 @@ export interface NodeTypeDefinition {
39
40
  type: string;
40
41
  /** Display name in palette */
41
42
  label?: string;
42
- /** Icon identifier (Material Icons, custom, etc.) */
43
+ /** Icon identifier (emoji, text, or symbol for fallback display) */
43
44
  icon?: string;
45
+ /**
46
+ * SVG icon definition for professional node icons.
47
+ * When set, renders an SVG icon in the node and palette instead of text/emoji.
48
+ * Use WORKFLOW_ICONS from '@utisha/graph-editor' or provide custom SvgIconDefinition.
49
+ * @example iconSvg: WORKFLOW_ICONS.process
50
+ */
51
+ iconSvg?: SvgIconDefinition;
44
52
  /** Palette category/group */
45
53
  category?: string;
46
54
  /** Angular component to render this node type */
@@ -49,6 +57,13 @@ export interface NodeTypeDefinition {
49
57
  configComponent?: Type<any>;
50
58
  /** Default data when node is created */
51
59
  defaultData: Record<string, any>;
60
+ /**
61
+ * Optional image URL for node icon. When set, renders an <image> element instead of text icon.
62
+ * Can be overridden per-instance via node.data['imageUrl'].
63
+ * Supports: SVG, PNG, JPG, data URLs, or any valid image URL.
64
+ * @example '/assets/icons/agent.svg'
65
+ * @example 'data:image/svg+xml;base64,...'
66
+ */
52
67
  /** Port definitions */
53
68
  ports?: PortConfig;
54
69
  /** Connection constraints */
@@ -0,0 +1,35 @@
1
+ /**
2
+ * SVG icon definition interface for custom node icons.
3
+ * Use this to define your own icons that match your design system.
4
+ *
5
+ * Example usage:
6
+ * const myIcons = {
7
+ * process: {
8
+ * viewBox: '0 0 24 24',
9
+ * fill: 'none',
10
+ * stroke: '#1D6A96',
11
+ * strokeWidth: 1.75,
12
+ * path: 'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z...'
13
+ * }
14
+ * };
15
+ */
16
+ export interface SvgIconDefinition {
17
+ /** SVG path data (d attribute) or full SVG markup */
18
+ path: string;
19
+ /** ViewBox dimensions (default: '0 0 24 24') */
20
+ viewBox?: string;
21
+ /** Fill color (default: 'none' for stroke-based icons) */
22
+ fill?: string;
23
+ /** Stroke color (default: currentColor) */
24
+ stroke?: string;
25
+ /** Stroke width (default: 2) */
26
+ strokeWidth?: number;
27
+ }
28
+ /**
29
+ * Generate inline SVG markup for an icon (for use in palette/toolbar)
30
+ */
31
+ export declare function renderIconSvg(icon: SvgIconDefinition, size?: number): string;
32
+ /**
33
+ * Generate data URL for an icon (for use as image source)
34
+ */
35
+ export declare function iconToDataUrl(icon: SvgIconDefinition, size?: number): string;
@@ -0,0 +1,57 @@
1
+ import { Graph } from '../graph.model';
2
+ import * as i0 from "@angular/core";
3
+ /**
4
+ * Service for managing undo/redo history of graph state.
5
+ *
6
+ * Uses a simple snapshot-based approach where each history entry
7
+ * is a deep copy of the entire graph state.
8
+ */
9
+ export declare class GraphHistoryService {
10
+ private history;
11
+ private historyIndex;
12
+ private maxHistorySize;
13
+ /** Signal to track if an undo/redo operation is in progress */
14
+ readonly isUndoRedo: import("@angular/core").WritableSignal<boolean>;
15
+ /**
16
+ * Initialize history with the given graph state.
17
+ * Clears any existing history.
18
+ */
19
+ init(graph: Graph): void;
20
+ /**
21
+ * Push a new state to the history stack.
22
+ * If we're not at the end of history, truncates future states.
23
+ * Prevents duplicate consecutive states.
24
+ *
25
+ * @returns true if state was pushed, false if it was a duplicate
26
+ */
27
+ push(graph: Graph): boolean;
28
+ /**
29
+ * Undo the last action.
30
+ * @returns The previous graph state, or null if nothing to undo
31
+ */
32
+ undo(): Graph | null;
33
+ /**
34
+ * Redo the last undone action.
35
+ * @returns The next graph state, or null if nothing to redo
36
+ */
37
+ redo(): Graph | null;
38
+ /**
39
+ * Complete an undo/redo operation.
40
+ * Call this after applying the state returned by undo() or redo().
41
+ */
42
+ completeUndoRedo(): void;
43
+ /** Check if undo is available */
44
+ canUndo(): boolean;
45
+ /** Check if redo is available */
46
+ canRedo(): boolean;
47
+ /** Clear history and reset to given state */
48
+ clear(graph: Graph): void;
49
+ /** Get current history size */
50
+ get size(): number;
51
+ /** Get current position in history (0-indexed) */
52
+ get position(): number;
53
+ /** Set maximum history size */
54
+ setMaxSize(size: number): void;
55
+ static ɵfac: i0.ɵɵFactoryDeclaration<GraphHistoryService, never>;
56
+ static ɵprov: i0.ɵɵInjectableDeclaration<GraphHistoryService>;
57
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utisha/graph-editor",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Configuration-driven visual graph editor for Angular 19+",
5
5
  "author": "Utisha",
6
6
  "license": "MIT",
package/public-api.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { GraphEditorComponent } from './lib/graph-editor.component';
2
2
  export type { Graph, GraphNode, GraphEdge, Position, NodeMetadata, EdgeMetadata, EdgeStyle, GraphMetadata } from './lib/graph.model';
3
3
  export type { GraphEditorConfig, NodesConfig, EdgesConfig, CanvasConfig, ValidationConfig, LayoutConfig, InteractionConfig, ThemeConfig, PaletteConfig, NodeTypeDefinition, PortConfig, PortDefinition, NodeConstraints, GridConfig, ZoomConfig, PanConfig, ValidationRule, ValidationError, LayoutOptions, ContextMenuConfig, ContextMenuItem, ContextMenuContext, SelectionState, ValidationResult, ContextMenuEvent } from './lib/graph-editor.config';
4
+ export type { SvgIconDefinition } from './lib/icons/workflow-icons';
5
+ export { renderIconSvg, iconToDataUrl } from './lib/icons/workflow-icons';