documentationfor yFiles for HTML 2.4

Using Node Styles for Port Candidates

Port Candidates are elements where edges may or may not connect during interactive edge creation. So an IPortCandidate represents a potential port.

Port Candidates can either be styled through CSS classes or by providing a custom IVisualTemplate that is queried using on the following ResourceKeys:

Resource Key CSS Classes
CANDIDATE_DRAWING_VALID_NON_FOCUSED_KEYUsed for rendering a valid, non-focused IPortCandidate.
CANDIDATE_DRAWING_VALID_FOCUSED_KEYUsed for rendering a valid, focused IPortCandidate.
CANDIDATE_DRAWING_INVALID_NON_FOCUSED_KEYUsed for rendering an invalid, non-focused IPortCandidate.
CANDIDATE_DRAWING_INVALID_FOCUSED_KEYUsed for rendering an invalid, focused IPortCandidate.

For example, a custom IVisualTemplate for valid, focused port candidates can be registered with the following call:

graphComponent.resources.set(
  DefaultPortCandidateDescriptor.CANDIDATE_DRAWING_VALID_FOCUSED_KEY,
  new IVisualTemplate({
    createVisual(context, bounds, dataObject) { /*...*/ },
    updateVisual(context, oldVisual, bounds, dataObject) { /*...*/ }
  })
)

It is also possible to use INodeStyles for rendering port candidates. For example the ShapeNodeStyle can be used with the following adapter class:

Adapter to use INodeStyles for port candidates.
class MyCandidateTemplate extends BaseClass(IVisualTemplate) {
  /**
   * @param {*} nodeStyle
   */
  constructor(nodeStyle) {
    super()
    // create a dummy node to render
    const node = new SimpleNode()
    node.style = nodeStyle
    // set the size of the port candidate here - it has to be centered at 0/0
    node.layout = new Rect(-5, -5, 10, 10)
    this.dummyNode = node
  }

  /**
   * @param {!IRenderContext} context
   * @param {!Rect} bounds
   * @param {!Object} dataObject
   * @returns {?SvgVisual}
   */
  createVisual(context, bounds, dataObject) {
    // delegate the rendering to the node style
    return this.dummyNode.style.renderer
      .getVisualCreator(this.dummyNode, this.dummyNode.style)
      .createVisual(context)
  }

  /**
   * @param {!IRenderContext} context
   * @param {!SvgVisual} oldVisual
   * @param {!Rect} bounds
   * @param {!Object} dataObject
   * @returns {?SvgVisual}
   */
  updateVisual(context, oldVisual, bounds, dataObject) {
    // delegate the rendering to the node style
    return this.dummyNode.style.renderer
      .getVisualCreator(this.dummyNode, this.dummyNode.style)
      .updateVisual(context, oldVisual)
  }
}class MyCandidateTemplate extends BaseClass(IVisualTemplate) {
  dummyNode: SimpleNode

  constructor(nodeStyle: any) {
    super()
    // create a dummy node to render
    const node = new SimpleNode()
    node.style = nodeStyle
    // set the size of the port candidate here - it has to be centered at 0/0
    node.layout = new Rect(-5, -5, 10, 10)
    this.dummyNode = node
  }

  createVisual(context: IRenderContext, bounds: Rect, dataObject: Object): SvgVisual | null {
    // @ts-ignore
    // delegate the rendering to the node style
    return this.dummyNode.style.renderer
      .getVisualCreator(this.dummyNode, this.dummyNode.style)
      .createVisual(context)
  }

  updateVisual(
    context: IRenderContext,
    oldVisual: SvgVisual,
    bounds: Rect,
    dataObject: Object
  ): SvgVisual | null {
    // @ts-ignore
    // delegate the rendering to the node style
    return this.dummyNode.style.renderer
      .getVisualCreator(this.dummyNode, this.dummyNode.style)
      .updateVisual(context, oldVisual)
  }
}

Then, an INodeStyle can be registered as renderer for the port candidates:

Use a ShapeNodeStyle to render the port candidates.
// style for valid, focused  port candidates
const validStyle = new ShapeNodeStyle({
  shape: 'diamond',
  fill: 'green',
  stroke: 'darkgreen'
})

// style for invalid, focused port candidates
const invalidStyle = new ShapeNodeStyle({
  shape: 'diamond',
  fill: 'darkorange',
  stroke: 'red'
})

// use adapter class with the ShapeNodeStyle instances to style the port candidates
graphComponent.resources.set(
  DefaultPortCandidateDescriptor.CANDIDATE_DRAWING_VALID_FOCUSED_KEY,
  new MyCandidateTemplate(validStyle)
)
graphComponent.resources.set(
  DefaultPortCandidateDescriptor.CANDIDATE_DRAWING_INVALID_FOCUSED_KEY,
  new MyCandidateTemplate(invalidStyle)
)