Skip to content
D
Documentation

Comments

reference
3 min readUpdated

Import these from @grafloria/renderer.

Functions

commentPinAccessibleName

The accessible name of a pin. This is the ONLY thing an AT user hears about a comment before they decide whether to open it, so it has to answer the question they are actually asking: what is this about, is it live, and is any of it new to me?

ts
function commentPinAccessibleName(thread: CommentThreadView): string

renderCommentPins

Build the pin layer.

Returns a <g> even when empty: a stable element keeps the patcher's keyed diff simple, and an empty <g> costs nothing to reconcile.

ts
function renderCommentPins(
  threads: readonly CommentThreadView[],
  options: CommentPinsOptions = {}
): VNode

Classes

CommentOverlayController

ts
class CommentOverlayController implements CommentSource

Methods

  • constructor( private readonly store: CommentStore, private readonly renderer: CommentRendererHost, private readonly options: CommentOverlayOptions = {} )
  • getThreads(): readonly CommentThreadView[] — The threads to draw. Called once per BUILT frame (never on a skipped one).
  • getPinOptions(): Pick<CommentPinsOptions, 'selectedThreadId' | 'showResolved' | 'radius'> — Local view state — selection and the resolved filter.

NOT keyboard focus. That is the RENDERER's a11yFocus, which already owns the roving tabindex for nodes and edges, and a comment pin is just a third kind of thing in the same one-tab-stop widget. Two authorities for "what is focused" is how you get two elements with tabindex=0, which is the exact bug the roving tabindex exists to prevent — so there is one, and it is the one that already existed.

  • select(threadId: string | null): void — Open a thread. Opening it is READING it, which is what "unread" means.
  • getSelected(): string | null
  • setShowResolved(show: boolean): void
  • markRead(threadId: string): void — Mark a thread read WITHOUT opening it (the "mark all read" affordance).
  • getInvalidationCount(): number — How many times the picture has been declared stale. A test asserts this moves.
  • dispose(): void

CommentPanelView

ts
class CommentPanelView

Methods

  • constructor( container: HTMLElement, private readonly store: CommentStore, options: CommentPanelOptions = {} )
  • getElement(): HTMLElement
  • getRebuildCount(): number
  • select(threadId: string | null, opts?: { focus?: boolean }): void — Which thread is open. Setting it re-renders and moves focus onto the conversation.
  • getSelected(): string | null
  • update(): boolean — Rebuild if — and only if — something the panel SHOWS has changed.

Safe on every frame. A drag, a pan and a zoom all change the diagram and change nothing here, and must therefore cost zero DOM operations.

  • dispose(): void

Interfaces

CommentOverlayOptions

ts
interface CommentOverlayOptions

Properties

NameTypeDefaultDescription
showResolved?boolean
onSelectionChange?(threadId: string | null) => voidNotified when the open thread changes (a host pans to it, opens the panel, …).

CommentPanelOptions

ts
interface CommentPanelOptions

Properties

NameTypeDefaultDescription
label?stringAccessible name of the region.
showResolved?booleanShow resolved threads too. Default false — a resolved thread is an answered one.
onSelect?(threadId: string | null) => voidCalled when the user picks a thread (the host selects it and pans to it).
onDismiss?() => voidCalled on Escape. The host MUST return focus to the canvas.
formatTime?(ms: number) => stringRender a wall-clock timestamp. Injectable so tests are not clock-dependent.

CommentPinsOptions

ts
interface CommentPinsOptions

Properties

NameTypeDefaultDescription
visibleRect?{ x: number; y: number; width: number; height: number }World rect currently on screen. Pins outside it are not built.
zoom?numberCurrent zoom, so the pin can be counter-scaled to a constant screen size.
selectedThreadId?string | nullThe thread whose panel is open — drawn selected, and aria-expanded=true.
focusedThreadId?string | nullThe comment the roving tabindex has focused, if any.
showResolved?booleanHide resolved threads (the default view — a resolved thread is answered).
radius?numberRadius in SCREEN pixels.

CommentRendererHost

The slice of the renderer this controller drives. Structural, so it is trivial to fake.

ts
interface CommentRendererHost

Members

  • setCommentSource(source: CommentSource | null): void
  • invalidateFrame(): void

CommentSource

What the renderer needs from a comment source. Deliberately tiny — the renderer must not know what a thread IS, only where the pins go and what they are called.

ts
interface CommentSource

Members

  • getThreads(): readonly CommentThreadView[] — The threads to draw. Called once per BUILT frame (never on a skipped one).
  • getPinOptions(): Pick<CommentPinsOptions, 'selectedThreadId' | 'showResolved' | 'radius'> — Local view state — selection and the resolved filter.

NOT keyboard focus. That is the RENDERER's a11yFocus, which already owns the roving tabindex for nodes and edges, and a comment pin is just a third kind of thing in the same one-tab-stop widget. Two authorities for "what is focused" is how you get two elements with tabindex=0, which is the exact bug the roving tabindex exists to prevent — so there is one, and it is the one that already existed.

Was this page helpful?

Comments — Grafloria