OWL
data_display
Default Command Item
Odoo 19 OWL component — Default Command Item (core)
Live preview
Interactive
Source excerpt
web/static/src/core/commands/command_palette.js
import { Dialog } from "@web/core/dialog/dialog";
import { useHotkey } from "@web/core/hotkeys/hotkey_hook";
import { _t } from "@web/core/l10n/translation";
import { KeepLast, Race } from "@web/core/utils/concurrency";
import { useAutofocus, useService } from "@web/core/utils/hooks";
import { scrollTo } from "@web/core/utils/scrolling";
import { fuzzyLookup } from "@web/core/utils/search";
import { debounce } from "@web/core/utils/timing";
import { isMacOS, isMobileOS } from "@web/core/browser/feature_detection";
import { highlightText } from "@web/core/utils/html";
import {
Component,
onWillStart,
onWillDestroy,
EventBus,
useRef,
useState,
markRaw,
useExternalListener,
} from "@odoo/owl";
const DEFAULT_PLACEHOLDER = _t("Search...");
const DEFAULT_EMPTY_MESSAGE = _t("No result found");
const FUZZY_NAMESPACES = ["default"];
/**
* @typedef {import("./command_service").Command} Command
*/
/**
* @typedef {Command & {
* Component?: Component;
* props?: object;
* }} CommandItem
*/
/**
* @typedef {{
* namespace?: string;
* provide: ()=>CommandItem[];
* }} Provider
*/
/**
* @typedef {{
* categories: string[];
* debounceDelay: number;
* emptyMessage: string;
* placeholder: string;
* }} NamespaceConfig
*/
/**
* @typedef {{
* configByNamespace?: {[namespace: string]: NamespaceConfig};
* FooterComponent?: Component;
* providers: Provider[];
* searchValue?: string;
* }} CommandPaletteConfig
*/
/**
* Util used to filter commands that are within category.
* Note: for the default category, also get all commands having invalid category.
*
* @param {string} categoryName the category key
* @param {string[]} categories
* @returns an array filter predicate
*/
function commandsWithinCategory(categoryName, categories) {
return (cmd) => {
const inCurrentCategory = categoryName === cmd.category;
const fallbackCategory = categoryName === "default" && !categories.includes(cmd.category);
return inCurrentCategory || fallbackCategory;
};
}
export class DefaultCommandItem extends Component {
static template = "web.DefaultCommandItem";
static props = {
slots: { type: Object, optional: true },
// Props send by the command palette:
hotkey: { type: String, optional: true },
hotkeyOptions: { type: String, optional: true },
name: { type: String, optional: true },
searchValue: { type: String, optional: true },
executeCommand: { type: Function, optional: true },
};
}
export class CommandPalette extends Component {
static template = "web.CommandPalette";
static components = { Dialog };
static lastSessionId = 0;
static props = {
bus: { type: EventBus, optional: true },
close: Function,
config: Object,
closeMe: { type: Function, optional: true },
};
setup() {
if (this.props.bus) {
const setConfig = ({ detail }) => this.setCommandPaletteConfig(detail);
this.props.bus.addEventListener(`SET-CONFIG`, setConfig);
onWillDestroy(() => this.props.bus.removeEventListener(`SET-CONFIG`, setConfig));
}
this.keyId = 1;
this.race = new Race();
this.keepLast = new KeepLast();
this._sessionId = CommandPalette.lastSessionId++;
this.DefaultCommandItem = DefaultCommandItem;
this.activeElement = useService("ui").activeElement;
this.inputRef = useAutofocus();
useHotkey("Enter", () => this.executeSelectedCommand(), { bypassEditableProtection: true });
useHotkey("Control+Enter", () => this.executeSelectedCommand(true), {
bypassEditableProtection: true,
Registry / API
- Registry name
DefaultCommandItem- Category
—- Module
web- Slug
default-command-item- Nav group
data_display