feat(VertexHandler): add margin and preview shape matching options#957
feat(VertexHandler): add margin and preview shape matching options#957
Conversation
Add new configuration options to CellHandler that allow: - Customizable selection margins via options - Preview shapes that match the actual cell shape including shapes defined in stencils Changes include: - Update getSelectionBounds to use margin from options - Update createSelectionShape to match cell shape styling - Add options support to CellHandler story examples - Improve JSDoc documentation for new options
WalkthroughThis change introduces stencil-based selection shape support for vertex handlers. New configuration fields ( Changes
Sequence DiagramsequenceDiagram
participant Caller
participant VertexHandler
participant VertexHandlerConfig
participant StencilShapeRegistry
participant Shape
Caller->>VertexHandler: createSelectionShape(bounds)
VertexHandler->>VertexHandlerConfig: check selectionShapeMatchVertex
alt selectionShapeMatchVertex = true
VertexHandler->>StencilShapeRegistry: getStencil(state)
alt Stencil found
StencilShapeRegistry-->>VertexHandler: stencil
VertexHandler->>Shape: construct from stencil
Shape-->>VertexHandler: shape
else No stencil
VertexHandler->>Shape: construct RectangleShape (fallback)
Shape-->>VertexHandler: shape
end
else selectionShapeMatchVertex = false
VertexHandler->>Shape: construct RectangleShape
Shape-->>VertexHandler: shape
end
VertexHandler->>VertexHandlerConfig: check margin
VertexHandler->>VertexHandler: getSelectionBounds(margin)
VertexHandler-->>Caller: selection Shape
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/core/src/view/handler/VertexHandler.ts (1)
54-55: Widen type declarations toShapefor fields assigned results ofcreateSelectionShape()The review comment is correct. The fields
selectionBorderandparentHighlightin VertexHandler.ts are declared asRectangleShape, but they receiveShapeinstances fromcreateSelectionShape()andcreateParentHighlightShape()(lines 431–459). This causes TypeScript strict type errors even though the runtime works.Additionally, this affects:
- EdgeHandler.ts line 205:
parentHighlight: RectangleShape | null- CellState.ts line 150:
parentHighlight: RectangleShape | null(shared across handlers)All four fields should be widened to
Shape:- selectionBorder: RectangleShape; + selectionBorder: Shape; - parentHighlight: RectangleShape | null = null; + parentHighlight: Shape | null = null;
🧹 Nitpick comments (3)
packages/core/src/view/handler/config.ts (1)
239-245: VertexHandlerConfig extensions are coherent with handler usageThe new
marginandselectionShapeMatchVertexfields, plus the clarifiedselectionDasheddocs, align with howVertexHandlerconsumes them. If you want to be extra explicit, you could mention thatmarginis interpreted in view (screen) units, but functionally this is fine as-is.Also applies to: 259-265, 273-281
packages/core/src/view/handler/VertexHandler.ts (2)
431-459: Harden createSelectionShape against missing state.shape and improve fallback behaviorThe new
selectionShapeMatchVertexbranch is great, but there are a couple of robustness details worth tightening:
new this.state.shape.constructor()assumesthis.state.shapeis always defined. If a consumer globally enablesselectionShapeMatchVertexand some vertices have nostate.shape(e.g. label-only/HTML cases), this will throw.- When falling back to
this.state.shape.constructor, the new shape doesn't getapply(this.state), so shape-specific style (e.g. arc size, rounded flags) won't be available on the selection outline, unlike the stencil path.You can make this safer and more consistent by:
- Guarding against a missing
state.shapeand falling back to the rectangular selection for those cases.- Applying the state to non-stencil shapes as well.
For example:
createSelectionShape(bounds: Rectangle): Shape { - let shape: Shape; + let shape: Shape; if (VertexHandlerConfig.selectionShapeMatchVertex) { - const stencil = StencilShapeRegistry.get(this.state.style.shape); - - if (stencil) { - shape = new Shape(stencil); - shape.apply(this.state); - } else { - // @ts-ignore known to work at runtime - shape = new this.state.shape.constructor(); - } + const stencil = StencilShapeRegistry.get(this.state.style.shape); + + if (stencil) { + shape = new Shape(stencil); + shape.apply(this.state); + } else if (this.state.shape) { + // @ts-expect-error constructor signatures differ per shape type but work at runtime + shape = new (this.state.shape.constructor as typeof Shape)(); + shape.apply(this.state); + } else { + shape = new RectangleShape( + Rectangle.fromRectangle(bounds), + NONE, + this.getSelectionColor() + ); + } } else { shape = new RectangleShape( Rectangle.fromRectangle(bounds), NONE, this.getSelectionColor() ); }The rest of the configuration (
outline,bounds, stroke, etc.) can remain as you have it.
411-419: Margin is not applied during interactive resize, only after reset/redraw
getSelectionBoundsexpands the initial selection bounds byVertexHandlerConfig.margin, andreset/redrawcopy that intothis.bounds. However, duringresizeVertexyou recomputethis.boundsdirectly from the unscaled geometry without reapplying the margin, anddrawPreview/getSelectionBorderBoundsusethis.boundsas-is.Net effect: for non-zero
margin, the selection border/handles will be flush with the vertex during drag, then “jump” outward by the margin oncereset/redrawruns.If you want the margin to be visually consistent while resizing, consider:
- Keeping
this.boundsas the geometry bounds; and- Applying the margin only in
getSelectionBorderBounds()(and, if desired, the sizer bounds) instead of baking it intogetSelectionBounds.That would avoid the jump without changing geometry math.
Also applies to: 1070-1080, 2035-2037
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
packages/core/src/view/event/InternalEvent.ts(1 hunks)packages/core/src/view/handler/VertexHandler.ts(8 hunks)packages/core/src/view/handler/config.ts(3 hunks)packages/core/src/view/shape/Shape.ts(3 hunks)packages/html/stories/CustomHandlesConfiguration.stories.ts(4 hunks)packages/html/stories/Stencils.stories.ts(2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: tbouffard
Repo: maxGraph/maxGraph PR: 849
File: packages/html/stories/DragSource.stories.js:98-101
Timestamp: 2025-06-13T07:48:10.300Z
Learning: User tbouffard prefers answers in English; avoid switching to other languages in future replies.
📚 Learning: 2025-04-24T12:33:36.243Z
Learnt from: tbouffard
Repo: maxGraph/maxGraph PR: 776
File: packages/core/src/view/Graph.ts:77-83
Timestamp: 2025-04-24T12:33:36.243Z
Learning: All register functions in maxGraph (`registerDefaultShapes`, `registerDefaultEdgeStyles`, `registerDefaultPerimeters`, and `registerDefaultEdgeMarkers`) have internal state flags to prevent duplicate registrations, making them inherently idempotent.
Applied to files:
packages/core/src/view/shape/Shape.ts
🧬 Code graph analysis (3)
packages/html/stories/Stencils.stories.ts (1)
packages/core/src/view/handler/config.ts (1)
VertexHandlerConfig(232-281)
packages/core/src/view/handler/config.ts (1)
packages/core/src/util/Constants.ts (1)
VERTEX_SELECTION_DASHED(181-181)
packages/core/src/view/handler/VertexHandler.ts (2)
packages/core/src/view/shape/stencil/StencilShapeRegistry.ts (1)
StencilShapeRegistry(46-46)packages/core/src/util/Constants.ts (1)
NONE(378-378)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: build
- GitHub Check: build (windows-2022)
- GitHub Check: build (ubuntu-24.04)
- GitHub Check: build (macos-14)
🔇 Additional comments (5)
packages/core/src/view/event/InternalEvent.ts (1)
232-236: Excellent JSDoc documentation improvements.The updated documentation uses proper
{@link}syntax for type references, which improves tooling support, IDE navigation, and documentation generation. The clarified description also better explains the method's behavior.packages/core/src/view/shape/Shape.ts (1)
207-255: Outline and pointer-events refactor looks soundThe updated outline handling in
createCanvas/paintand the new pointer-events flags (shapePointerEvents,stencilPointerEvents) preserve existing behavior while making outline-only rendering and event-capture rectangles more explicit. I don't see correctness or regression risks here.Also applies to: 470-499, 575-587, 612-617
packages/html/stories/Stencils.stories.ts (1)
79-82: Stencil story wiring for selection-shape matching is appropriateUsing
VertexHandlerConfig.selectionShapeMatchVertex = truein this story cleanly activates the new stencil-aware selection border behavior, and the added rotation comment is a nice hint without changing behavior. No issues here.Also applies to: 88-93
packages/core/src/view/handler/VertexHandler.ts (1)
131-137: Minor doc and API-shape tweaks look goodThe clarifications around
livePreview,verticalOffset,isCenteredEvent(now explicitly unused params), and the extended JSDoc for handle padding/redrawing all match current behavior and improve readability without changing semantics.Also applies to: 168-171, 370-372, 1721-1765
packages/html/stories/CustomHandlesConfiguration.stories.ts (1)
88-93: Story config and object-form vertex creation align well with the new handler optionsUsing
VertexHandlerConfig.margin = 6,selectionDashed = false, andselectionShapeMatchVertex = truenicely showcases the new selection-border behavior in this custom-handles story. The switch to object-forminsertVertex({ value, x, y, width, height, style })for several vertices makes their capabilities (rotatable, pre-rotated, non-resizable, different perimeters/shapes) clearer while preserving the original graph structure. I don't see any functional regressions here.Also applies to: 95-97, 126-156, 166-176, 188-193
| // @ts-ignore known to work at runtime | ||
| shape = new this.state.shape.constructor(); |
There was a problem hiding this comment.
suggestion: review coderabit suggestion
| // @ts-ignore known to work at runtime | |
| shape = new this.state.shape.constructor(); | |
| // @ts-expect-error constructor signatures differ per shape type but work at runtime | |
| shape = new (this.state.shape.constructor as typeof Shape)(); | |
| shape.apply(this.state); |
| /** | ||
| * Margin to be added between the selection border and the vertex's bounding box. | ||
| * @default 0 | ||
| * @since 0.22.0 |
| * This mimics the behavior of {@link CellRenderer}: first check if the shape is available in the {@link StencilShapeRegistry}, otherwise use the shape of the vertex. | ||
| * | ||
| * @default false | ||
| * @since 0.22.0 |



Add new configuration options to VertexHandler that allow:
Changes include:
Screenshots
Preview_uses_shape_of_the_vertex.mp4
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.