blueprint icon indicating copy to clipboard operation
blueprint copied to clipboard

How to pass custom props to ContextMenu2 content?

Open devinhalladay opened this issue 3 years ago • 2 comments

Environment

  • Package version(s):
    "@blueprintjs/core": "4.0.0-beta.11",
    "@blueprintjs/icons": "4.0.0-beta.6",
    "@blueprintjs/popover2": "^0.12.9",
    "@blueprintjs/select": "4.0.0-beta.11",
    
  • Operating System: MacOS 12.2
  • Browser name and version: Firefox (latest), Chrome (latest)

Question

I'm creating a canvas-based PKM tool, and need to add a context menu to every item on the canvas. I've done this by wrapping all the elements in a single <ContextMenu2 /> with a content prop, and using the advanced rendering pattern to provide a custom wrapper for my layout.

<ContextMenu2
  content={
    <Menu>
      <MenuItem
        icon="send-to-graph"
        // Need access to "block" prop
        onClick={() => handleBlockClick(props.block)}
        text="Add to canvas"
      />
      <MenuItem
        intent="danger"
        icon="trash"
        // Need access to "block" prop
        onClick={() => props.handleBlockClick(props.block)}
        text={`Delete ${props.block.__typename}`}
      />
    </Menu>
  }
>
  {(ctxMenuProps) => (
    <div
      className={classNames('grid', ctxMenuProps.className)}
      onContextMenu={ctxMenuProps.onContextMenu}
      ref={ctxMenuProps.ref}
    >
      {blocks.map((block) => (
        <Card
          key={block.id}
          interactive={false}
          onClick={(e) => { ... }}
        >
          {ctxMenuProps.popover}
          <div>
            block content
          </div>
        </Card>
      ))}
    </div>
  )}
</ContextMenu2>

You'll notice the blocks.map() line, which renders the items on the canvas, each of which should have a context menu that passes a block prop to the content of the context menu. That way, when the context menu is opened for a block, I can click Delete block and have access to its ID in the <Menu />

Is this possible?

devinhalladay avatar Jan 29 '22 18:01 devinhalladay

I was hoping I could do something similar to the advanced context menu render pattern:

<ContextMenu2
  content={(ctxContentProps) => (
    <Menu>
      <MenuItem
        icon="send-to-graph"
        // Access my block prop which was put on the cloned ctxMenuProps.popover
        onClick={() => handleBlockClick(ctxContentProps.block)}
        text="Add to canvas"
      />
      {/* other menu items */}
    </Menu>
  )}
>
  {(ctxMenuProps) => (
    <div
      className={classNames('grid', ctxMenuProps.className)}
      onContextMenu={ctxMenuProps.onContextMenu}
      ref={ctxMenuProps.ref}
    >
      {blocks.map((block) =>
        // Clone the popover element and pass the block prop
        React.cloneElement(
          ctxMenuProps.popover,
          { block: ctxMenuProps.popover }
          // block content here
        )
      )}
    </div>
  )}
</ContextMenu2>;


devinhalladay avatar Jan 29 '22 18:01 devinhalladay

Update: I was able to achieve this by adding a state value that is set within onContextMenu, and then used within the ContextMenu2 content prop. Still curious if there is a better way to do this though.

devinhalladay avatar Jan 30 '22 00:01 devinhalladay