Skip to content

Commit d524d65

Browse files
committed
chore: update rules and documentation for components and events
1 parent 53305d4 commit d524d65

File tree

9 files changed

+320
-12
lines changed

9 files changed

+320
-12
lines changed

.clinerules

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,10 @@ flowchart TD
112112

113113
Note: When triggered by **update memory bank**, I MUST review every memory bank file, even if some don't require updates. Focus particularly on activeContext.md and progress.md as they track current state.
114114

115-
REMEMBER: After every memory reset, I begin completely fresh. The Memory Bank is my only link to previous work. It must be maintained with precision and clarity, as my effectiveness depends entirely on its accuracy.
115+
REMEMBER: After every memory reset, I begin completely fresh. The Memory Bank is my only link to previous work. It must be maintained with precision and clarity, as my effectiveness depends entirely on its accuracy.
116+
117+
# These are references to the actual rule files in .cursor/rules/
118+
project-rules: .cursor/rules/project-rules.mdc
119+
components-rules: .cursor/rules/components-rules.mdc
120+
layer-rules: .cursor/rules/layer-rules.mdc
121+
event-model-rules: .cursor/rules/event-model-rules.mdc

.cursor/rules/components-rules.mdc

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
---
2-
description: Best pratices for useing canvas components
2+
description: Best practices for using canvas components in @gravity-ui/graph
33
globs:
44
alwaysApply: false
55
---
66

7-
## General Component Rules
7+
# General Component Rules
88
- use `setState` instead direct setting new value via `this.state =`
99
- use `setProps` instead direct setting new value via `this.props =`
1010
- use `Component.create(props)` method to creating a children component instead `new Component(props)`
11-
- for creating c children component use only method `updateChildren`
11+
- for creating children component use only method `updateChildren`
1212
```typescript
1313
protected updateChildren() {
1414
return [Component.create(...props)]
@@ -22,19 +22,53 @@ protected updateChildren() {
2222
- Override `stateChanged` method to optimize rendering and perform specific actions when data changes.
2323
- Use `shouldRender = false` to skip unnecessary renders.
2424

25-
## Block Component Rules
25+
# Canvas Component Structure
26+
Canvas components are located in src/components/canvas/ and divided into several categories:
27+
- blocks/ - graph blocks
28+
- connections/ - connections between blocks
29+
- anchors/ - anchors for connections
30+
- layers/ - layers for rendering various elements
31+
- groups/ - element grouping
32+
33+
# React Integration
34+
For integration of Canvas components with React, wrappers are used in the src/react-component/ directory. These components allow the use of Canvas in React applications.
35+
36+
# Rendering Layers
37+
Canvas rendering is organized in layers with different priorities:
38+
- Background layer (low priority)
39+
- Block layer (medium priority)
40+
- Connection layer (medium priority)
41+
- Selection and interactive elements layer (high priority)
42+
43+
# Zoom Levels
44+
The scaling system automatically switches between Canvas and HTML/React:
45+
- Low zoom (ECameraScaleLevel.LOW) - everything is rendered on Canvas
46+
- Medium zoom (ECameraScaleLevel.MEDIUM) - schematic view with basic interactivity
47+
- High zoom (ECameraScaleLevel.HIGH) - full React components for rich interactivity
48+
49+
# Block Component Rules
2650
- Custom block components must extend the `Block` class from `Block.ts`.
2751
- Do not transform original coordinates onto world coordinates in components.
2852
- Scale line widths based on camera zoom: `ctx.lineWidth = Math.round(2 / this.context.camera.getCameraScale())`.
2953
- Implement both schematic and detailed views for components.
3054
- Handle selection state appropriately in render methods.
3155
- Optimize text rendering based on zoom level.
3256

33-
## Connection Component Rules
57+
# Connection Component Rules
3458
- Use appropriate curve calculations for connection paths.
3559
- Update connection path when connected blocks change position.
3660
- Implement proper hit detection for connection lines.
3761
- Handle connection selection state visually.
3862

39-
## Documentation Rule
40-
- Do not mention these rules in documentation or comments unless specifically requested by the user.
63+
# Performance Considerations
64+
When working with Canvas components:
65+
- Minimize the number of redraws
66+
- Use caching for complex calculations
67+
- Apply only necessary transformations
68+
- Render only visible elements in the current viewport
69+
70+
# Custom Rendering
71+
For creating custom components:
72+
- Use the renderBlock property in GraphCanvas to override block display
73+
- Implement custom components by inheriting from base classes
74+
- Monitor performance when creating complex custom renderers

.cursor/rules/cursor-rules.mdc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
description:
3+
globs: *.mdc
4+
alwaysApply: false
5+
---
6+
# Cursor Rules Location
7+
8+
Rules for placing and organizing Cursor rule files in the repository.
9+
10+
---
11+
description: Standards for placing Cursor rule files in the correct directory
12+
filters:
13+
# Match any .mdc files
14+
- type: file_extension
15+
pattern: "\\.mdc$"
16+
# Match files that look like Cursor rules
17+
- type: content
18+
pattern: "(?s)<rule>.*?</rule>"
19+
# Match file creation events
20+
- type: event
21+
pattern: "file_create"
22+
---
23+
24+
actions:
25+
- type: reject
26+
conditions:
27+
- pattern: "^(?!\\.\\/\\.cursor\\/rules\\/.*\\.mdc$)"
28+
message: "Cursor rule files (.mdc) must be placed in the .cursor/rules directory"
29+
30+
- type: suggest
31+
message: |
32+
When creating Cursor rules:
33+
34+
1. Always place rule files in PROJECT_ROOT/.cursor/rules/:
35+
```
36+
.cursor/rules/
37+
├── your-rule-name.mdc
38+
├── another-rule.mdc
39+
└── ...
40+
```
41+
42+
2. Follow the naming convention:
43+
- Use kebab-case for filenames
44+
- Always use .mdc extension
45+
- Make names descriptive of the rule's purpose
46+
47+
3. Directory structure:
48+
```
49+
PROJECT_ROOT/
50+
├── .cursor/
51+
│ └── rules/
52+
│ ├── your-rule-name.mdc
53+
│ └── ...
54+
└── ...
55+
```
56+
57+
4. Never place rule files:
58+
- In the project root
59+
- In subdirectories outside .cursor/rules
60+
- In any other location
61+
62+
examples:
63+
- input: |
64+
# Bad: Rule file in wrong location
65+
rules/my-rule.mdc
66+
my-rule.mdc
67+
.rules/my-rule.mdc
68+
69+
# Good: Rule file in correct location
70+
.cursor/rules/my-rule.mdc
71+
output: "Correctly placed Cursor rule file"
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
description: Rules and best practices for working with the event model in @gravity-ui/graph (Based on docs/system/events.md and docs/react/usage.md)
3+
globs:
4+
alwaysApply: false
5+
---
6+
7+
## Event System Overview
8+
The event system allows communication between different parts of the graph.
9+
The primary event hub is the main **`Graph` instance**. Events related to specific components (like blocks or connections) are also typically emitted by the main `Graph` instance, carrying information about the target component in the event detail.
10+
11+
The system uses **`CustomEvent`**, and interaction follows a `.on`/`.off`/`.emit` pattern on the `Graph` instance.
12+
13+
## Key Concepts
14+
- **Event Emitter:** The main `Graph` instance is the primary emitter.
15+
- **Event Object:** Instances of `CustomEvent`. Data is in **`event.detail`**.
16+
- **Target Component:** For interaction events (e.g., `block:select`, `click`), `event.detail.target` often references the specific `GraphComponent` interacted with.
17+
- **Source Event:** `event.detail.sourceEvent` may contain the original low-level DOM/Canvas event.
18+
- **Event Types:** String names. See `docs/system/events.md` for a list.
19+
- **Subscription API (Core):** Uses **`graph.on(event, callback, options)`**.
20+
- **Unsubscription API (Core):** Uses **`graph.off(event, callback)`**. **Crucial for cleanup.**
21+
- **Control Flow:** Standard `event.preventDefault()` and `event.stopPropagation()` can be used on the `CustomEvent` object.
22+
23+
## Working with Events
24+
25+
### 1. Subscribing to Graph Events (Core API)
26+
- **How:** Use the main `Graph` instance.
27+
- **API:** `graph.on(eventName, callback, options)`.
28+
- **Context:** Access the graph instance via `this.context.graph` in non-React components.
29+
- **Use Case:** Reacting to global changes or interactions outside of React components. Remember to manage unsubscription manually (e.g., in `unmount`).
30+
```typescript
31+
// Example: Global camera update listener in a non-React component
32+
const unsubscribeCamera = this.context.graph.on('camera:update', (event: CustomEvent</* TCameraState */>) => {
33+
const cameraState = event.detail;
34+
console.log('Camera moved:', cameraState);
35+
});
36+
// In unmount(): if (unsubscribeCamera) unsubscribeCamera();
37+
```
38+
39+
### 2. Handling Events in React (`<GraphCanvas>` wrapper)
40+
- **Obtaining the Graph Instance:** Within React components, the `Graph` instance needed for event handling (especially with `useGraphEvent`) is typically obtained using the **`useGraph()`** hook: `const { graph } = useGraph(config);`.
41+
- **Method A (Recommended): `useGraphEvent` Hook:** The primary way to listen to graph events within React function components.
42+
- **Requires:** The `graph` instance obtained from `useGraph()`.
43+
- **Pros:** Automatically handles subscription/unsubscription; provides `detail` and `event` objects.
44+
- **Cons:** Only usable within React function components.
45+
```jsx
46+
import { useGraph, useGraphEvent, GraphCanvas } from '@gravity-ui/graph';
47+
48+
function MyReactComponent() {
49+
const { graph } = useGraph(/* config */);
50+
51+
useGraphEvent(graph, 'block:select', (detail, event) => {
52+
console.log('React Hook: Block selected:', detail.target?.id);
53+
});
54+
55+
// ... other useGraphEvent calls
56+
57+
return <GraphCanvas graph={graph} /* ... */ />;
58+
}
59+
```
60+
- **Method B: `onEventName` Props:** Use dedicated props on **`<GraphCanvas>`** for common, predefined events.
61+
- **Requires:** Passing the `graph` instance to `<GraphCanvas>`.
62+
- **Pros:** Declarative, simple for common cases.
63+
- **Cons:** Limited to the specific events exposed as props.
64+
```jsx
65+
import { GraphCanvas } from '@gravity-ui/graph';
66+
67+
// ... assume graph instance is available
68+
69+
<GraphCanvas
70+
graph={graph}
71+
onBlockSelectionChange={(event: CustomEvent<SelectionEvent<TBlockId>>) => {
72+
console.log('React Prop: Blocks selected:', event.detail.list);
73+
}}
74+
// ... other props
75+
/>
76+
```
77+
78+
### General Best Practices
79+
- **Prioritize `useGraphEvent`:** In React, prefer the hook for its robustness and lifecycle management.
80+
- **Always Unsubscribe (Core API):** If using `graph.on` directly (outside React hooks), ensure cleanup with `graph.off()`.
81+
- **Use `event.detail`:** Access event-specific data here.
82+
- **Check `event.detail.target`:** Identify the specific component involved.
83+
- **Emit Custom Events:** Use `graph.emit(...)` to broadcast custom global events.
84+
- **Performance:** Avoid heavy logic in listeners.

.cursor/rules/graph-structure.mdc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
description: Graph structure and architecture rules
3+
globs:
4+
alwaysApply: false
5+
---
6+
7+
## Graph Architecture
8+
This file contains rules and information about the architecture of the graph library:
9+
10+
- The graph is built on a layered architecture
11+
- Canvas is used for high performance rendering
12+
- React components are used for detailed interaction
13+
- The system automatically switches between rendering modes

.cursor/rules/layer-rules.mdc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
description: Rules for working with rendering layers in @gravity-ui/graph
3+
alwaysApply: false
4+
---
5+
6+
## Layer System Overview
7+
Layers are fundamental to the Canvas rendering pipeline in @gravity-ui/graph. They organize drawable components and manage rendering order and performance. They ensure that elements like backgrounds, connections, blocks, and interactive elements are drawn in the correct sequence.
8+
9+
**Primary Goal: Extensibility:** Layers are the primary mechanism for extending the library's core functionality. If a user request involves adding new visual elements or behaviors without altering the base logic, creating a new custom layer is the preferred approach.
10+
11+
## Key Layer Concepts
12+
- **Purpose:** Each layer is responsible for a specific aspect of the graph, which can include **rendering visual elements** (e.g., `BackgroundLayer`, `ConnectionLayer`) or **managing behavior and logic** (e.g., handling specific user interactions, managing non-visual state). A layer might not have a direct visual representation but still participate in the graph's lifecycle and logic.
13+
- **Location:** Core layer logic and specific layer implementations are found in `src/components/canvas/layers/` and potentially base classes/services like `src/services/Layer.ts`.
14+
- **Rendering Order/Priority:** Layers are rendered/processed in a specific sequence determined by the main graph component. The order is crucial for visual correctness and logical flow (e.g., connections below blocks, interaction handlers processed before rendering). Typical order might be: Background -> Connections -> Blocks -> Groups -> Behavior/Interaction Layers -> Highlight Layers.
15+
- **Performance:** Layers optimize rendering and processing by:
16+
- Drawing/processing only elements or logic relevant to that layer.
17+
- Potentially culling elements outside the current viewport.
18+
- Managing redraws or logic updates based on relevant state changes.
19+
20+
## Working with Layers
21+
- **Layers are Components:** Treat layers as specialized components. They follow the standard component lifecycle (`mount`, `unmount`, `render`, `stateChanged`, etc.) and **all general component rules (see `components-rules`) apply to layers as well**. A layer's `render` method might be empty if it only handles behavior.
22+
- **Creating New Layers:**
23+
- If adding new types of visual elements or behaviors, create a new layer class.
24+
- New layers should ideally extend a base `Layer` class or a suitable existing layer, inheriting common functionality.
25+
- Register the new layer in the main graph's processing pipeline in the correct order.
26+
- **Modifying Existing Layers:**
27+
- When changing how elements are rendered or behaviors are managed, modify the relevant methods (e.g., `render`, event handlers) of the corresponding layer.
28+
- Ensure efficiency; avoid unnecessary computations or drawing invisible elements.
29+
- **Interaction & Behavior:** Layers are suitable for encapsulating specific interaction logic (e.g., drag-and-drop handling, tool activation). These layers might not draw anything but listen to events and modify the graph state.
30+
- **Event Propagation & Camera Interaction:** Since layers are often added directly to the root container (and not nested within the `GraphLayer` which handles core event delegation and contains the `Camera`), mouse events intended for camera interactions (like panning via click/drag) might be intercepted by the layer. To ensure the camera receives these events, you may need to override the layer's `getParent()` method to directly return the camera component: `return this.props.graph.getGraphLayer().$.camera;`. This effectively bypasses the standard hierarchy for event bubbling, delegating the event to the camera. *Note:* This is a workaround; be mindful of potential side effects on other event handling within your layer. See the `BlockGroups` layer (`src/components/canvas/groups/BlockGroups.ts`) for a practical example.
31+
- **State Management:** Layers typically access the graph's central state store (`store/`) to get the data they need and to dispatch changes. Use reactive patterns (signals) to trigger updates when relevant data changes.
32+
- **Cleanup:** Implement necessary cleanup in the layer's `destroy` or `unmount` method to release resources, remove listeners, etc.

.cursor/rules/project-rules.mdc

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
description:
3+
globs:
4+
alwaysApply: true
5+
---
6+
## Project Overview
7+
@gravity-ui/graph is a graph visualization library that combines the best of both worlds: Canvas for high performance when viewing the full graph and HTML/React for rich interactions when zoomed in.
8+
9+
## Project Structure
10+
Project structure:
11+
- src/ - source code
12+
- api/ - API for interacting with the graph
13+
- components/ - Canvas components
14+
- canvas/ - contains blocks, connections, anchors and layers for rendering
15+
- lib/ - helper libraries
16+
- plugins/ - plugins for extending functionality
17+
- react-component/ - React wrappers for Canvas components
18+
- services/ - services, including camera and others
19+
- store/ - graph state storage
20+
- stories/ - examples of component usage for Storybook
21+
- utils/ - utility functions
22+
- docs/ - project documentation
23+
- .storybook/ - Storybook configuration
24+
25+
## Technologies
26+
Technology stack:
27+
- TypeScript - main development language
28+
- React - for creating user interfaces
29+
- Canvas API - for rendering high-performance graphics
30+
- Storybook - for component development and testing
31+
- Jest - for unit testing
32+
- ESLint/Prettier - for maintaining code quality and style
33+
34+
## Key Components
35+
Key components:
36+
- Graph - main class for graph management (src/graph.ts)
37+
- GraphCanvas - React component for displaying the graph (src/react-component/GraphCanvas.tsx)
38+
- Block - component for representing a graph block
39+
- Connection - component for connections between blocks
40+
- Anchor - components for connection attachment points
41+
42+
## Rendering System
43+
Rendering system:
44+
- Full graph is rendered on Canvas for performance
45+
- When zooming in, HTML/React mode is automatically enabled for interactive elements
46+
- Uses a smart system that tracks visible blocks and renders only them in React
47+
48+
## Development Guidelines
49+
Development guidelines:
50+
- To run the project locally, use `npm run storybook`
51+
- To build the project, use `npm run build`
52+
- Tests are run via `npm run test`
53+
- Use TypeScript for typing all code
54+
- Follow component architecture principles
55+
56+
## File Naming Conventions
57+
File naming conventions:
58+
- Components: ComponentName.tsx
59+
- Utilities: utilityName.ts
60+
- Tests: ComponentName.test.ts
61+
- Styles: ComponentName.css
62+
- Types and interfaces: start with T or I (TBlock, IService)

.roorules

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
**See ./clinerules for more instructions**
1+
**See ./clinerules for more instructions**
2+
3+
project-rules: .cursor/rules/project-rules.mdc
4+
components-rules: .cursor/rules/components-rules.mdc
5+
graph-structure: .cursor/rules/graph-structure.mdc
6+
layer-rules: .cursor/rules/layer-rules.mdc
7+
event-model-rules: .cursor/rules/event-model-rules.mdc

docs/system/events.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ interface GraphMouseEvent<E extends Event = Event> = CustomEvent<{
8888

8989
## React Integration
9090

91-
The GraphComponent in React provides two ways to handle events:
91+
The GraphCanvas component in React provides two ways to handle events:
9292

9393
1. Using `onEventName` props for common events
94-
2. Using `graphRef` and the `on` method for all events
94+
2. Using the `graph` instance (obtained via `useGraph`) and the `on` method for all events
9595

9696
### Common Event Props
9797

@@ -123,7 +123,7 @@ const YourPrettyGraphComponent = () => {
123123
[]
124124
);
125125

126-
return <GraphComponent onBlockSelectionChange={onBlockSelectionChange} />;
126+
return <GraphCanvas onBlockSelectionChange={onBlockSelectionChange} />;
127127
};
128128
```
129129

0 commit comments

Comments
 (0)