-
Notifications
You must be signed in to change notification settings - Fork 6
feat: added touch device support #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Reviewer's GuideThis PR replaces separate mouse and touch handling with unified PointerEvent support across the graph, updating event types, handlers, utilities, drag listener, type definitions, and CSS to enable basic single-touch interactions. Class diagram for updated event handling typesclassDiagram
class GraphLayer {
- prevTargetComponent: EventedComponent
- pointerStartTarget?: EventedComponent
- pointerStartEvent?: PointerEvent
- pointerPressed: boolean
- eventByTargetComponent?: EventedComponent | PointerEvent
- capturedTargetComponent?: EventedComponent
+ handleEvent(e: Event): void
+ dispatchNativeEvent(type: GraphPointerEventNames, event: PointerEvent | GraphPointerEvent, targetComponent?): void
+ applyEventToTargetComponent(event: PointerEvent | GraphPointerEvent, target?): void
+ updateTargetComponent(event: PointerEvent, force = false): void
+ onRootPointerMove(event: PointerEvent): void
+ handlePointerDownEvent(event: PointerEvent): void
+ onRootPointerStart(event: PointerEvent): void
+ onRootPointerEnd(event: PointerEvent): void
+ tryEmulateClick(event: PointerEvent, target?): void
}
class BlockController {
- block: Block
+ constructor(block: Block)
}
class Camera {
- lastDragEvent?: PointerEvent
+ handlePointerDownEvent(event: PointerEvent): void
+ onDragStart(event: PointerEvent): void
+ onDragUpdate(event: PointerEvent): void
}
class Anchor {
+ handleEvent(event: PointerEvent | KeyboardEvent)
}
class BaseConnection {
+ handleEvent(event)
}
class MiniMapLayer {
+ handlePointerDownEvent(rootEvent: PointerEvent)
+ onCameraDrag(event: PointerEvent)
}
class SelectionLayer {
+ handlePointerDown(nativeEvent: GraphPointerEvent)
+ updateSelectionRender(event: PointerEvent)
+ startSelectionRender(event: PointerEvent)
+ endSelectionRender(event: PointerEvent)
}
class NewBlockLayer {
+ handlePointerDown(nativeEvent: GraphPointerEvent)
+ onStartNewBlock(event: PointerEvent, block: Block)
+ onMoveNewBlock(event: PointerEvent)
+ onEndNewBlock(event: PointerEvent, point: TPoint)
}
class DevToolsLayer {
- pointerMoveListener
- pointerEnterListener
- pointerLeaveListener
+ handlePointerMove(event: PointerEvent)
+ handlePointerEnter()
+ handlePointerLeave()
}
class Graph {
+ getPointInCameraSpace(event: PointerEvent)
}
class GraphComponent {
+ onDragStart?: (_event: PointerEvent) => void | boolean
+ onDragUpdate?: (diff, _event: PointerEvent) => void
+ onDrop?: (_event: PointerEvent) => void
+ isDraggable?: (event: PointerEvent) => boolean
}
Class diagram for updated event utility functionsclassDiagram
class UtilsFunctions {
+ getXY(root: HTMLElement, event: Event | WheelEvent | PointerEvent): [number, number]
+ getCoord(event: PointerEvent, coord: string)
+ getEventDelta(e1, e2)
+ isMetaKeyEvent(event: MouseEvent | PointerEvent | KeyboardEvent): boolean
+ isShiftKeyEvent(event: PointerEvent | KeyboardEvent): boolean
+ isAltKeyEvent(event: PointerEvent | KeyboardEvent): boolean
+ getEventSelectionAction(event: PointerEvent)
+ createCustomDragEvent(eventType: string, e): CustomEvent
+ dispatchEvents(comps, e)
+ addEventListeners(instance: EventTarget, mapEventsToFn?: Record<string, (event: CustomEvent | PointerEvent) => void>): () => void
}
Class diagram for updated event type definitionsclassDiagram
class GraphPointerEvent {
+ target?: EventedComponent
+ sourceEvent: PointerEvent
+ pointerPressed?: boolean
}
class GraphPointerEventNames {
<<enumeration>>
click
dblclick
pointerdown
pointerenter
pointerleave
}
class BaseGraphEventDefinition {
+ click(event: GraphPointerEvent)
+ dblclick(event: GraphPointerEvent)
+ pointerdown(event: GraphPointerEvent)
+ pointerenter(event: GraphPointerEvent)
+ pointerleave(event: GraphPointerEvent)
}
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @pavel-klimov - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `src/components/canvas/layers/graphLayer/GraphLayer.ts:29` </location>
<code_context>
- "mouseup",
- "touchend",
+ "pointerdown",
+ "pointerenter",
+ "pointermove",
"click",
"dblclick",
</code_context>
<issue_to_address>
Including 'pointerenter' and 'pointermove' as bubbling events may not match their actual event propagation.
'pointerenter' and 'pointerleave' do not bubble, so including them here may cause issues. Consider handling them separately or only during the capturing phase.
</issue_to_address>
### Comment 2
<location> `src/components/canvas/layers/graphLayer/GraphLayer.ts:315` </location>
<code_context>
-
- if (this.canEmulateClick && (event.type === "click" || event.type === "dblclick")) {
- this.applyEventToTargetComponent(new MouseEvent(event.type, event), this.pointerStartTarget);
+ this.applyEventToTargetComponent(new PointerEvent(event.type, event), target);
}
}
</code_context>
<issue_to_address>
Reconstructing a PointerEvent from an existing event may lose some properties.
Only properties included in eventInitDict are copied, so some original event properties may be lost. Consider re-dispatching the original event or using a more thorough cloning method if full fidelity is required.
</issue_to_address>
### Comment 3
<location> `src/components/canvas/blocks/controllers/BlockController.ts:17` </location>
<code_context>
import { ESelectionStrategy } from "../../../../utils/types/types";
import { Block } from "../Block";
+const handlePointerDownEvent = (event: PointerEvent, block: Block, self: any) => {
+ const blockState = selectBlockById(block.context.graph, block.props.id);
+ const allowChangeBlockGeometry = isAllowChangeBlockGeometry(
</code_context>
<issue_to_address>
Consider moving the event handler into the class as a private method to avoid passing extra parameters and centralize logic.
```suggestion
Rather than a top‐level `handlePointerDownEvent(event, block, self)`, you can simplify by moving it into the class as a private arrow or bound method. This removes the extra parameters (`block`/`self`) and keeps all logic in one place:
```ts
export class BlockController {
private block: Block;
constructor(block: Block) {
this.block = block;
addEventListeners(block as EventTarget, {
click: this.onClick,
pointerdown: this.onPointerDown, // ← use the private method directly
});
}
private onClick = (event: PointerEvent) => {
event.stopPropagation();
const { connectionsList } = this.block.context.graph.rootStore;
if (!isMetaKeyEvent(event) && connectionsList.$selectedConnections.value.size) {
connectionsList.resetSelection();
}
this.block.context.graph.api.selectBlocks(
[this.block.props.id],
!isMetaKeyEvent(event),
!isMetaKeyEvent(event) ? ESelectionStrategy.REPLACE : ESelectionStrategy.APPEND,
);
};
private onPointerDown = (event: PointerEvent) => {
const blk = this.block;
const graph = blk.context.graph;
const state = selectBlockById(graph, blk.props.id);
const allow = isAllowChangeBlockGeometry(
blk.getConfigFlag("canChangeBlockGeometry") as ECanChangeBlockGeometry,
state.selected
);
if (!allow) return;
event.stopPropagation();
const store = graph.rootStore.blocksList;
const selectedStates = getSelectedBlocks(state, store);
const components = selectedStates.map(s => s.getViewComponent());
dragListener(blk.context.ownerDocument)
.on(EVENTS.DRAG_START, e => {
graph.getGraphLayer().captureEvents(this);
dispatchEvents(components, createCustomDragEvent(EVENTS.DRAG_START, e));
})
.on(EVENTS.DRAG_UPDATE, e => {
dispatchEvents(components, createCustomDragEvent(EVENTS.DRAG_UPDATE, e));
})
.on(EVENTS.DRAG_END, e => {
graph.getGraphLayer().releaseCapture();
dispatchEvents(components, createCustomDragEvent(EVENTS.DRAG_END, e));
});
};
}
```
Benefits:
- No need to pass `block` or `this` around.
- All event logic lives inside the controller.
- Easier to follow control flow and keep member access via `this.block`.
```
</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Preview is ready. |
bf4b1ce
to
c676502
Compare
Switched from MouseEvent to PointerEvent to add basic touch device support (single-finger interaction). Complex gestures (multi-touch) are not supported; behaviour remains the same as with mouse input.
Summary by Sourcery
Switch to a unified PointerEvent-based input model to enable basic single-finger touch interaction across the graph canvas, blocks, connections, and plugins.
New Features:
Enhancements: