Clawscale

Context menu

A ContextMenu opens a menu of actions where the user right-clicks. It wraps a target element and shows a PopoverNext at the pointer.

Usage#

import { ContextMenu, Menu, MenuItem } from "@clawscale/react";

ContextMenu renders a div around its children and passes extra props to it. Change the element with tagName.

Examples#

Basic#

Pass a Menu as content. Right-click the card.

orders_daily
1,284,112 rows, updated 4 min ago

Right-click for actions.

import { Card, Classes, ContextMenu, EntityTitle, Menu, MenuDivider, MenuItem } from "@clawscale/react";

export default function ContextMenuBasic() {
  return (
    <ContextMenu
      content={
        <Menu>
          <MenuItem icon="eye-open" text="Preview rows" />
          <MenuItem icon="duplicate" text="Copy table path" />
          <MenuItem icon="edit" text="Rename" />
          <MenuDivider />
          <MenuItem icon="trash" intent="danger" text="Delete dataset" />
        </Menu>
      }
    >
      <Card style={{ width: 300 }}>
        <EntityTitle icon="database" title="orders_daily" subtitle="1,284,112 rows, updated 4 min ago" />
        <p className={Classes.TEXT_MUTED} style={{ margin: "12px 0 0" }}>
          Right-click for actions.
        </p>
      </Card>
    </ContextMenu>
  );
}

Table rows#

tagName="tr" makes each row its own target. Right-click a row.

RunPipelineStatus
4812ingest-ordersRunning
4811sessionize-eventsFailed
4810nightly-rollupSucceeded
import { ContextMenu, HTMLTable, Menu, MenuDivider, MenuItem, Tag } from "@clawscale/react";

const runs = [
  { id: 4812, pipeline: "ingest-orders", status: "Running", intent: "primary" },
  { id: 4811, pipeline: "sessionize-events", status: "Failed", intent: "danger" },
  { id: 4810, pipeline: "nightly-rollup", status: "Succeeded", intent: "success" },
] as const;

export default function ContextMenuTable() {
  return (
    <HTMLTable compact interactive style={{ width: "100%" }}>
      <thead>
        <tr>
          <th>Run</th>
          <th>Pipeline</th>
          <th>Status</th>
        </tr>
      </thead>
      <tbody>
        {runs.map((run) => (
          <ContextMenu
            key={run.id}
            tagName="tr"
            content={
              <Menu>
                <MenuItem icon="document-open" text={`Open run ${run.id}`} />
                <MenuItem icon="console" text="View logs" />
                <MenuItem icon="refresh" text="Rerun" />
                <MenuDivider />
                <MenuItem disabled={run.status !== "Running"} icon="stop" intent="danger" text="Cancel run" />
              </Menu>
            }
          >
            <td>{run.id}</td>
            <td>{run.pipeline}</td>
            <td>
              <Tag intent={run.intent} minimal>
                {run.status}
              </Tag>
            </td>
          </ContextMenu>
        ))}
      </tbody>
    </HTMLTable>
  );
}

Imperative API#

showContextMenu opens a menu at any position, for example from a canvas or chart event handler. hideContextMenu closes it. The menu renders in a separate React root, so it cannot read your React context.

events_rawsessionsorders_dailyrevenue_by_region
import { Card, Menu, MenuItem, showContextMenu, Tag } from "@clawscale/react";
import type { MouseEvent } from "react";

const tables = ["events_raw", "sessions", "orders_daily", "revenue_by_region"];

export default function ContextMenuImperative() {
  const openMenu = (event: MouseEvent<HTMLElement>, table: string) => {
    event.preventDefault();
    showContextMenu({
      content: (
        <Menu>
          <MenuItem icon="eye-open" text={`Preview ${table}`} />
          <MenuItem icon="data-lineage" text="Show upstream tables" />
          <MenuItem icon="duplicate" text="Copy table name" />
        </Menu>
      ),
      targetOffset: { left: event.clientX, top: event.clientY },
    });
  };

  return (
    <Card style={{ display: "flex", flexWrap: "wrap", gap: 8, maxWidth: 420 }}>
      {tables.map((table) => (
        <Tag key={table} icon="th" interactive minimal onContextMenu={(event) => openMenu(event, table)}>
          {table}
        </Tag>
      ))}
    </Card>
  );
}

Without a wrapper#

To skip the wrapper element, pass a function as children. Spread its class name, handler and ref onto your element and render its popover.

<ContextMenu content={menu}>
  {(target) => (
    <div className={target.className} onContextMenu={target.onContextMenu} ref={target.ref}>
      {target.popover}
      orders_daily
    </div>
  )}
</ContextMenu>

content can also be a function. It receives isOpen, targetOffset and mouseEvent, for menus that depend on where the user clicked.

Theme#

Menus follow the dark color scheme through the bp6-dark class that ClawscaleProvider puts on <html>.

Props interface#

interface ContextMenuProps extends Omit<React.HTMLAttributes<HTMLElement>, "children" | "className" | "content" | "onContextMenu">, React.RefAttributes<any>, Props@clawscale/react
PropsDescription
childrenrequired
React.ReactNode | ((props: ContextMenuChildrenProps) => React.ReactElement)
The context menu target. This may optionally be a render function so you can use component state to render the target.
className
string
A space-delimited list of class names to pass along to a child element.
Inherited from Props
contentrequired
React.JSX.Element | ((props: ContextMenuContentProps) => React.JSX.Element | undefined) | undefined
Menu content. This will usually be a Blueprint <Menu> component. This optionally functions as a render prop so you can use component state to render content.
disabled
boolean = false
Whether the context menu is disabled.
onClose
() => void
Callback invoked when the popover overlay closes.
onContextMenu
React.MouseEventHandler<HTMLElement>
An optional context menu event handler. This can be useful if you want to do something with the mouse event unrelated to rendering the context menu itself, especially if that involves setting React state (which is an error to do in the render code path of this component).
popoverProps
ContextMenuPopoverOptions
A limited subset of props to forward along to the popover overlay generated by this component.
tagName
keyof React.JSX.IntrinsicElements = "div"
HTML tag to use for container element. Only used if this component's children are specified as React node(s), not when it is a render function (in that case, you get to render whatever tag you wish).

ContextMenuPopover is the lower-level popover that ContextMenu and showContextMenu render. It is controlled with isOpen and targetOffset.

interface ContextMenuPopoverProps extends ContextMenuPopoverOptions@clawscale/react
PropsDescription
contentrequired
React.JSX.Element
isDarkTheme
boolean
isOpenrequired
boolean
onClose
() => void
onClosed
(node: HTMLElement) => void
Lifecycle method invoked just after the CSS _close_ transition ends but before the child has been removed from the DOM. Receives the DOM element of the child being closed.
Inherited from OverlayLifecycleProps
onClosing
(node: HTMLElement) => void
Lifecycle method invoked just before the CSS _close_ transition begins on a child. Receives the DOM element of the child being closed.
Inherited from OverlayLifecycleProps
onOpened
(node: HTMLElement) => void
Lifecycle method invoked just after the CSS _open_ transition ends. Receives the DOM element of the child being opened.
Inherited from OverlayLifecycleProps
onOpening
(node: HTMLElement) => void
Lifecycle method invoked just after mounting the child in the DOM but just before the CSS _open_ transition begins. Receives the DOM element of the child being opened.
Inherited from OverlayLifecycleProps
placement
Placement = "auto"
The placement (relative to the target) at which the popover should appear. Mutually exclusive with position prop. Prefer using this over position, as it more closely aligns with Popper.js semantics.
Inherited from PopoverSharedProps
popoverClassName
string
A space-delimited string of class names applied to the popover element.
Inherited from PopoverSharedProps
popoverRef
React.Ref<HTMLElement>
DOM ref attached to the Classes.POPOVER element.
Inherited from PopoverSharedProps
rootBoundary
RootBoundary
A root boundary element supplied to the "flip" and "preventOverflow" modifiers. This is a shorthand for overriding Popper.js modifier options with the modifiers prop.
Inherited from PopoverSharedProps
targetOffsetrequired
Offset | undefined
transitionDuration
number = 300
Indicates how long (in milliseconds) the overlay's enter/leave transition takes. This is used by React CSSTransition to know when a transition completes and must match the duration of the animation in CSS. Only set this prop if you override Blueprint's default transitions with new transitions of a different length.
Inherited from OverlayableProps
interface ShowContextMenuOptions@clawscale/react

Options for specifying how an imperatively created context menu should be rendered to the DOM.

PropsDescription
container
HTMLElement = document.body
A new DOM element will be created and appended to this container.
render
ShowContextMenuDOMRenderer
A function render the React component onto a newly created DOM element. This should return a function which unmounts the rendered element from the DOM.