UNPKG

@wordpress/commands

Version:
277 lines (276 loc) 7.63 kB
// packages/commands/src/components/command-menu.js import { Command, useCommandState } from "cmdk"; import clsx from "clsx"; import { useSelect, useDispatch } from "@wordpress/data"; import { useState, useEffect, useRef, useCallback, useMemo } from "@wordpress/element"; import { __ } from "@wordpress/i18n"; import { Modal, TextHighlight, __experimentalHStack as HStack, privateApis as componentsPrivateApis } from "@wordpress/components"; import { store as keyboardShortcutsStore, useShortcut } from "@wordpress/keyboard-shortcuts"; import { Icon, search as inputIcon } from "@wordpress/icons"; import { store as commandsStore } from "../store"; import { unlock } from "../lock-unlock"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var { withIgnoreIMEEvents } = unlock(componentsPrivateApis); var inputLabel = __("Search commands and settings"); function CommandMenuLoader({ name, search, hook, setLoader, close }) { const { isLoading, commands = [] } = hook({ search }) ?? {}; useEffect(() => { setLoader(name, isLoading); }, [setLoader, name, isLoading]); if (!commands.length) { return null; } return /* @__PURE__ */ jsx(Fragment, { children: commands.map((command) => /* @__PURE__ */ jsx( Command.Item, { value: command.searchLabel ?? command.label, keywords: command.keywords, onSelect: () => command.callback({ close }), id: command.name, children: /* @__PURE__ */ jsxs( HStack, { alignment: "left", className: clsx("commands-command-menu__item", { "has-icon": command.icon }), children: [ command.icon && /* @__PURE__ */ jsx(Icon, { icon: command.icon }), /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx( TextHighlight, { text: command.label, highlight: search } ) }) ] } ) }, command.name )) }); } function CommandMenuLoaderWrapper({ hook, search, setLoader, close }) { const currentLoaderRef = useRef(hook); const [key, setKey] = useState(0); useEffect(() => { if (currentLoaderRef.current !== hook) { currentLoaderRef.current = hook; setKey((prevKey) => prevKey + 1); } }, [hook]); return /* @__PURE__ */ jsx( CommandMenuLoader, { hook: currentLoaderRef.current, search, setLoader, close }, key ); } function CommandMenuGroup({ isContextual, search, setLoader, close }) { const { commands, loaders } = useSelect( (select) => { const { getCommands, getCommandLoaders } = select(commandsStore); return { commands: getCommands(isContextual), loaders: getCommandLoaders(isContextual) }; }, [isContextual] ); if (!commands.length && !loaders.length) { return null; } return /* @__PURE__ */ jsxs(Command.Group, { children: [ commands.map((command) => /* @__PURE__ */ jsx( Command.Item, { value: command.searchLabel ?? command.label, keywords: command.keywords, onSelect: () => command.callback({ close }), id: command.name, children: /* @__PURE__ */ jsxs( HStack, { alignment: "left", className: clsx("commands-command-menu__item", { "has-icon": command.icon }), children: [ command.icon && /* @__PURE__ */ jsx(Icon, { icon: command.icon }), /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx( TextHighlight, { text: command.label, highlight: search } ) }) ] } ) }, command.name )), loaders.map((loader) => /* @__PURE__ */ jsx( CommandMenuLoaderWrapper, { hook: loader.hook, search, setLoader, close }, loader.name )) ] }); } function CommandInput({ isOpen, search, setSearch }) { const commandMenuInput = useRef(); const _value = useCommandState((state) => state.value); const selectedItemId = useMemo(() => { const item = document.querySelector( `[cmdk-item=""][data-value="${_value}"]` ); return item?.getAttribute("id"); }, [_value]); useEffect(() => { if (isOpen) { commandMenuInput.current.focus(); } }, [isOpen]); return /* @__PURE__ */ jsx( Command.Input, { ref: commandMenuInput, value: search, onValueChange: setSearch, placeholder: inputLabel, "aria-activedescendant": selectedItemId } ); } function CommandMenu() { const { registerShortcut } = useDispatch(keyboardShortcutsStore); const [search, setSearch] = useState(""); const isOpen = useSelect( (select) => select(commandsStore).isOpen(), [] ); const { open, close } = useDispatch(commandsStore); const [loaders, setLoaders] = useState({}); useEffect(() => { registerShortcut({ name: "core/commands", category: "global", description: __("Open the command palette."), keyCombination: { modifier: "primary", character: "k" } }); }, [registerShortcut]); useShortcut( "core/commands", /** @type {import('react').KeyboardEventHandler} */ withIgnoreIMEEvents((event) => { if (event.defaultPrevented) { return; } event.preventDefault(); if (isOpen) { close(); } else { open(); } }), { bindGlobal: true } ); const setLoader = useCallback( (name, value) => setLoaders((current) => ({ ...current, [name]: value })), [] ); const closeAndReset = () => { setSearch(""); close(); }; if (!isOpen) { return false; } const isLoading = Object.values(loaders).some(Boolean); return /* @__PURE__ */ jsx( Modal, { className: "commands-command-menu", overlayClassName: "commands-command-menu__overlay", onRequestClose: closeAndReset, __experimentalHideHeader: true, contentLabel: __("Command palette"), children: /* @__PURE__ */ jsx("div", { className: "commands-command-menu__container", children: /* @__PURE__ */ jsxs(Command, { label: inputLabel, children: [ /* @__PURE__ */ jsxs("div", { className: "commands-command-menu__header", children: [ /* @__PURE__ */ jsx( Icon, { className: "commands-command-menu__header-search-icon", icon: inputIcon } ), /* @__PURE__ */ jsx( CommandInput, { search, setSearch, isOpen } ) ] }), /* @__PURE__ */ jsxs(Command.List, { label: __("Command suggestions"), children: [ search && !isLoading && /* @__PURE__ */ jsx(Command.Empty, { children: __("No results found.") }), /* @__PURE__ */ jsx( CommandMenuGroup, { search, setLoader, close: closeAndReset, isContextual: true } ), search && /* @__PURE__ */ jsx( CommandMenuGroup, { search, setLoader, close: closeAndReset } ) ] }) ] }) }) } ); } export { CommandMenu, CommandMenuGroup, CommandMenuLoaderWrapper }; //# sourceMappingURL=command-menu.js.map