Skip to main content

Indicators

Selection indicators are the on-canvas chrome that surrounds the current selection โ€” the selection frame, handles, and the floating panel that follows it. The drawing lives in @oh-just-another/state and the React surface in @oh-just-another/react-ui.

Overlay passโ€‹

renderOverlay from @oh-just-another/state paints all interaction chrome โ€” selection frames (oriented per shape rotation), resize/rotate handles, lasso, link highlights, peer cursors โ€” onto a render target in one pass, styled by an OverlayStyle (start from DEFAULT_OVERLAY_STYLE). Handle geometry helpers (handlePosition, hitHandle, HANDLE_SIZE) are exported separately โ€” see Handles.

import { renderOverlay, DEFAULT_OVERLAY_STYLE, type OverlayStyle } from "@oh-just-another/state";

Floating selection panelโ€‹

@oh-just-another/react-ui provides SelectionFloatingPanel, the panel that tracks the active selection bounds (via floating-ui, flipping sides when space runs out) and hosts the property quick actions. It reads the live selection from the diagram context; offset, placement, and edge insets are optional props.

import { SelectionFloatingPanel } from "@oh-just-another/react-ui";

function Overlay() {
return <SelectionFloatingPanel />;
}

What the panel shows depends on the selection:

  • One element โ€” the full control set of its type, a comment button and the element link.
  • Several elements โ€” the intersection of the members' multi-selection sets: two shapes keep the shape controls (minus the link), a shape + a text share the text controls (font, style, colour โ€” written through Editor.updateTextStyle / updateTextProps, which route to a shape's label or a text element's own style), a shape + a frame share nothing. Mixed types add a Filter that narrows the selection to one type. The tail gains Arrange (align, distribute from three) and Group.
  • A group โ€” its children's shared controls; Ungroup replaces Group and align / distribute stay disabled until something else joins the selection. Double-click a child to edit inside the group: the toolbar then targets the children and hides Group / Ungroup; Esc or a click outside leaves.

Cursorsโ€‹

The canvas cursor is computed from the interaction state (computeCursor) and uses system keywords only:

StateCursor
Idle, hover over empty canvas or an element (selected or not)default
Dragging an element, link, waypoint, caption or commentdefault
Resize handle (hover / drag)ns / ew / nwse / nesw-resize, following the shape's rotation
Rotate handlegrab / grabbing
Link start dot, drawing a link from an anchorcrosshair
Link endpoint / waypoint handlegrab
Caption of the selected linktext
Rubber-band selectioncrosshair
Hand tool, Space, panning, read-only boardgrab / grabbing
Rect / ellipse / frame / edge / brush / laser / crop tools, colour pickercrosshair
Text tool, in-canvas text editingtext
Erasernone (the overlay paints the eraser ring)
Comment pinpointer

Every outcome has a CursorRole; a host can replace the image per role with editor.setCursorOverride(role, { url, url2x, hotspot, fallback }) (DPR-aware via image-set) or reset it with null.

The full <Editor> from @oh-just-another/editor wires these indicators up by default; reach for the lower-level pieces above only when building a custom overlay or a headless rendering of the selection state.