Skip to content

Triggers

The menu that slash commands and @ mentions open in the composer. Lists, searches, submenus and pills, on the menu recipe.

  • <aparte-triggers>
  • @aparte/plugin-triggers
On this page

aparte-triggers opens a menu when a trigger character is typed in the composer: / for commands, @ for mentions, any other you declare. It draws nothing until one fires, and the focus stays in the field the whole time.

bash
npm install @aparte/plugin-triggers
html
<aparte-composer>
  <aparte-triggers></aparte-triggers>
  <aparte-composer-input></aparte-composer-input>
  <aparte-composer-send></aparte-composer-send>
</aparte-composer>
ts
import '@aparte/plugin-triggers';

document.querySelector('aparte-triggers')!.triggers = [
  { char: '/', at: 'message', commands: true },
  { char: '@', kind: 'mention', items: [{ id: 'anna', label: 'Anna' }] },
];

With no triggers set, / opens at the start of a message and lists the commands your plugins add. Commands and mentions is the full guide: rows, searches, submenus, pills, commands.

Each panel is an aparte-menu, the menu recipe, with role="listbox". The row the keyboard is on wears aria-selected="true". A pill in the field and in a sent message is an aparte-tag with the class aparte-inline-token and data-kind set to the token’s kind:

css
.aparte-inline-token[data-kind='mention'] {
  --aparte-surface-2: var(--aparte-accent-soft);
}
ExportWhat it is
AparteTriggerOne entry of the triggers property: char, at, items, commands, match, kind, debounce, allowSpaces, maxItems
AparteTriggerItemOne row of a menu
AparteTriggerSourceA list asked on demand: (query, { signal, trigger }), returning rows or a promise of rows
AparteTriggerCommandsWhat an app decides about the commands: exclude, override
AparteTriggerContextWhat a row’s run receives
AparteCommandsOptionsThe options of setupCommands(), in @aparte/plugin-triggers/commands

Three pure functions, usable without a page, to test your setup:

ts
import { detectTrigger, matchRows, scoreItem, type TriggerHit, type TriggerRow } from '@aparte/plugin-triggers';

const trigger = { char: '@', items: [{ id: 'anna', label: 'Anna' }] };

const hit: TriggerHit | null = detectTrigger('hello @an', 9, [trigger]); // what the caret is in
const rows: TriggerRow[] = matchRows(trigger.items, 'an', trigger);      // the rows, best first
const score = scoreItem(trigger.items[0]!, 'an');                        // 3 for a prefix, 0 for no match

<aparte-triggers> · class AparteTriggers

Properties

PropertyTypeDescription
triggersreadonly AparteTrigger[]What opens a menu, and with what. By default / at the start of a message, listing the commands plugins add.
isOpen (readonly)booleanA menu is open.

Methods

MethodDescription
close(): voidClose the menu.

Events

EventTypeDescription
aparte-trigger-selectCustomEvent<AparteTriggerSelectEventDetail>A row was chosen. Cancelable: preventDefault() leaves the draft untouched and runs nothing.
aparte-collect-commandsCustomEvent<AparteCollectCommandsEventDetail>A menu with commands is opening.

CSS variables

VariableDefaultDescription
--aparte-menu-max-height320pxHeight at which a list starts to scroll.