Skip to content

Contributing Dialogs ​

Dialogs are interactive overlays triggered by keyboard shortcuts. The vis domain provides table, filter, targeted navigation, and description settings dialogs. You can add your own.

DialogContribution<P> ​

ts
interface DialogContribution<P> {
  id: string;
  label: string;
  applicableRoles?: readonly string[];
  triggerKey?: string;
  render?: (runtime: NavigationRuntime<P>, navNode: NavNode) => DialogRenderResult;
}
PropertyTypeRequiredDescription
idstringyesUnique identifier (e.g., 'table', 'filter').
labelstringyesDisplay name shown in the help dialog.
applicableRolesstring[]noWhich roles can open this dialog. Omit for all roles.
triggerKeystringnoKeyboard key that opens the dialog (e.g., 't', 'f').
renderfunctionnoProduces the dialog content.

DialogRenderResult ​

ts
interface DialogRenderResult {
  title: string;
  description?: string;
  content: unknown;
  onSubmit?: () => void;
}
PropertyTypeRequiredDescription
titlestringyesDialog heading, announced by screen readers.
descriptionstringnoAdditional context below the title.
contentunknownyesThe dialog body. In the Solid.js renderer, this is a JSX element.
onSubmit() => voidnoCalled when the dialog is submitted (if applicable).

The content type is unknown because the core doesn't depend on a specific UI framework. The Solid.js renderer expects Solid JSX elements.

Writing a dialog ​

ts
import type { DialogContribution } from 'olli-core';
import type { MyPayload } from './spec.js';

export function myInfoDialog(): DialogContribution<MyPayload> {
  return {
    id: 'info',
    label: 'Element info',
    triggerKey: 'i',
    applicableRoles: ['element'],
    render: (runtime, navNode) => {
      const edge = navNode.hyperedgeId
        ? runtime.getHyperedge(navNode.hyperedgeId)
        : undefined;
      const el = edge?.payload?.element;

      return {
        title: `Info: ${el?.label ?? 'Unknown'}`,
        description: el?.category ? `Category: ${el.category}` : undefined,
        content: null, // or a JSX element for richer content
      };
    },
  };
}

Registering dialogs ​

Add to your domain object:

ts
export const myDomain: OlliDomain<MySpec, MyPayload> = {
  name: 'my-domain',
  toHypergraph: lowerMySpec,
  dialogs: [myInfoDialog()],
};

Or register directly:

ts
runtime.registerDialog(myInfoDialog());

DialogRegistry ​

ts
interface DialogRegistry<P> {
  register(dialog: DialogContribution<P>): void;
  list(): readonly DialogContribution<P>[];
  byId(id: string): DialogContribution<P> | undefined;
}

The renderer uses dialogs.list() to build the help dialog and dialogs.byId(id) to look up a specific dialog.

How the renderer uses dialogs ​

The renderer's keybinding system checks registered dialogs:

  1. When a key is pressed, it looks for a dialog with a matching triggerKey.
  2. If applicableRoles is set, it checks whether the focused node's role matches.
  3. If both match, it calls render(runtime, navNode) to produce the dialog content.
  4. The dialog is opened as a modal overlay with the returned title, description, and content.

Next ​

Released under the BSD-3-Clause License.