ComponentUpdated in v15

Command Palette

A ⌘K search box that finds and runs your app's actions.

Installation

Import the class, and @use the entry point in your global styles: without it the component renders unstyled.

import { Component } from '@angular/core';
import { WrCommandPalette } from 'ngwr/command-palette';

@Component({ imports: [WrCommandPalette] })
export class MyComponent {}

Basic usage

Add one palette to your app shell; mod+k opens it. (picked) fires after the item's own action().

<wr-command-palette
  [items]="commands"
  trigger="mod+k"
  (picked)="onPicked($event)"
/>

Live demo

Press ⌘⇧K / Ctrl+Shift+K: the site header owns ⌘K, and when two palettes share a hotkey the first one wins.

Nothing picked yet.

Programmatic control

Bind [(open)]; [trigger]="null" turns off the hotkey.

<button (click)="open.set(true)">Open palette</button>

<wr-command-palette
  [items]="commands"
  [(open)]="open"
  [trigger]="null"
  (picked)="onPicked($event)"
/>

Backed by a server

Fetch on (searchChange) and feed the result into [items]. serverSearch turns off the built-in filter; [loading] shows “Searching…”.

<wr-command-palette
  [items]="results()"
  [(query)]="query"
  [loading]="loading()"
  serverSearch
  [debounceMs]="250"
  (searchChange)="search($event)"
/>

API

NameDescriptionTypeDefault
paletteLabelAccessible name. Falls back to commandPalette.label, then 'Command palette'.string | nullnull
itemsItems shown in the palette.readonly WrCommandItem[][]
openControlled open state (two-way bindable).booleanfalse
triggerGlobal hotkey that opens the palette. null disables auto-binding.WrHotkeySpec | null'mod+k'
placeholderSearch input placeholder. Falls back to commandPalette.placeholder.string | nullnull
emptyTextText shown when no items match. Falls back to commandPalette.noResults.string | nullnull
loadingTextOverrides the commandPalette.loading catalog string, shown instead of {@link emptyText} while {@link loading} is true.string | nullnull
closeOnPickAuto-close on (picked). Set false to keep open.booleantrue
(picked)Fires when the user commits an item (Enter / click).WrCommandItem—
queryThe search text. Two-way, so a host can seed it, clear it, or read what the user is looking for — the palette used to keep this to itself, which is what made it impossible to back with anything but a static items array. Writing it does NOT re-run a host's search on its own: the debounced {@link searchChange} output is what fires, on exactly the cadence {@link debounceMs} sets.string''
serverSearchThe item list is already scoped to the query upstream — skip the built-in client-side filter. Set it when [items] is fed from a server via the {@link searchChange} output. Without this, a backend that ranks or tolerates typos (returning rows whose labels do not literally contain the query) would have those rows hidden again on the client — the same rule wr-select follows, and the same reason.booleanfalse
loadingA host-driven search is in flight. Swaps the empty row for {@link loadingText} — an async palette that says "No results" between every keystroke and its answer is stating something false.booleanfalse
debounceMsHow long the query must settle before {@link searchChange} fires, in ms. A search request per keystroke is what exhausts a metered search backend: typing select is six requests at 0 and roughly one at 250.number250
(searchChange)The settled query. This is the hook for a server-backed palette: dispatch on it, then feed the result into [items] with [serverSearch] set. Fires on the {@link debounceMs} cadence, NOT per keystroke — queryChange is the per-keystroke signal if that is what you want.string—

Types

NameDescriptionTypeDefault
WrCommandItem{ id, label, description?, group?, icon?, keywords?, shortcut?, action? }interface—
[wrCommandPaletteFooter]Slot for a strip under the list: a hint, a link, a key legend.content slot—

CSS variables

Set these on the component's selector or a wrapper, not on :root: the defaults are declared on ngwr/command-palette's own element.

VariableDefaultDeclared on
--wr-command-palette-body-paddingvar(--wr-space-sm).wr-command-palette__body
--wr-command-palette-empty-font-sizevar(--wr-text-sm).wr-command-palette__empty
--wr-command-palette-empty-padding-xvar(--wr-space-lg).wr-command-palette__empty
--wr-command-palette-empty-padding-yvar(--wr-space-xl).wr-command-palette__empty
--wr-command-palette-font-familyvar(--wr-font-family-base).wr-command-palette
--wr-command-palette-footer-font-sizevar(--wr-text-xs).wr-command-palette__footer
--wr-command-palette-footer-gapvar(--wr-space-sm).wr-command-palette__footer
--wr-command-palette-footer-padding-xvar(--wr-space-lg).wr-command-palette__footer
--wr-command-palette-footer-padding-yvar(--wr-space-sm).wr-command-palette__footer
--wr-command-palette-group-padding-yvar(--wr-space-xs).wr-command-palette__group
--wr-command-palette-group-title-font-sizevar(--wr-text-xs).wr-command-palette__group-title
--wr-command-palette-group-title-font-weightvar(--wr-font-weight-semibold).wr-command-palette__group-title
--wr-command-palette-group-title-letter-spacingvar(--wr-tracking-wide).wr-command-palette__group-title
--wr-command-palette-group-title-padding-bottomvar(--wr-space-xs).wr-command-palette__group-title
--wr-command-palette-group-title-padding-topvar(--wr-space-sm).wr-command-palette__group-title
--wr-command-palette-group-title-padding-xvar(--wr-space-md).wr-command-palette__group-title
--wr-command-palette-hint-font-sizevar(--wr-text-xs).wr-command-palette__hint
--wr-command-palette-hint-font-weightvar(--wr-font-weight-medium).wr-command-palette__hint
--wr-command-palette-hint-letter-spacingvar(--wr-tracking-wide).wr-command-palette__hint
--wr-command-palette-hint-padding-xvar(--wr-control-padding-x-sm).wr-command-palette__hint
--wr-command-palette-hint-padding-yvar(--wr-control-padding-y-sm).wr-command-palette__hint
--wr-command-palette-hint-radiusvar(--wr-border-radius-sm).wr-command-palette__hint
--wr-command-palette-option-description-font-sizevar(--wr-text-xs).wr-command-palette__option-description
--wr-command-palette-option-description-margin-topvar(--wr-space-xs).wr-command-palette__option-description
--wr-command-palette-option-font-sizevar(--wr-text-sm).wr-command-palette__option
--wr-command-palette-option-gapvar(--wr-space-md).wr-command-palette__option
--wr-command-palette-option-label-font-weightvar(--wr-font-weight-medium).wr-command-palette__option-label
--wr-command-palette-option-line-heightvar(--wr-leading-tight).wr-command-palette__option
--wr-command-palette-option-padding-xvar(--wr-space-md).wr-command-palette__option
--wr-command-palette-option-padding-yvar(--wr-space-sm).wr-command-palette__option
--wr-command-palette-option-radiusvar(--wr-border-radius-sm).wr-command-palette__option
--wr-command-palette-option-shortcut-font-sizevar(--wr-text-xs).wr-command-palette__option-shortcut
--wr-command-palette-option-shortcut-font-weightvar(--wr-font-weight-medium).wr-command-palette__option-shortcut
--wr-command-palette-option-shortcut-padding-xvar(--wr-control-padding-x-sm).wr-command-palette__option-shortcut
--wr-command-palette-option-shortcut-padding-yvar(--wr-control-padding-y-sm).wr-command-palette__option-shortcut
--wr-command-palette-option-shortcut-radiusvar(--wr-border-radius-sm).wr-command-palette__option-shortcut
--wr-command-palette-padding-bottomvar(--wr-space-lg).wr-command-palette
--wr-command-palette-padding-top6rem.wr-command-palette
--wr-command-palette-padding-xvar(--wr-space-lg).wr-command-palette
--wr-command-palette-panel-radiusvar(--wr-border-radius-base).wr-command-palette__panel
--wr-command-palette-search-font-sizevar(--wr-text-base).wr-command-palette__search
--wr-command-palette-search-gapvar(--wr-space-md).wr-command-palette__search
--wr-command-palette-search-paddingvar(--wr-space-lg).wr-command-palette__search

Dependencies

Angular CDKa11y, overlay
Built onWrHotkey, Icon, Overlay

See also