@wordpress/commands
Version:
Handles the commands menu.
289 lines (284 loc) • 9.14 kB
JavaScript
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.CommandMenu = CommandMenu;
exports.CommandMenuGroup = CommandMenuGroup;
exports.CommandMenuLoaderWrapper = CommandMenuLoaderWrapper;
var _cmdk = require("cmdk");
var _clsx = _interopRequireDefault(require("clsx"));
var _data = require("@wordpress/data");
var _element = require("@wordpress/element");
var _i18n = require("@wordpress/i18n");
var _components = require("@wordpress/components");
var _keyboardShortcuts = require("@wordpress/keyboard-shortcuts");
var _icons = require("@wordpress/icons");
var _store = require("../store");
var _jsxRuntime = require("react/jsx-runtime");
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
const inputLabel = (0, _i18n.__)('Search commands and settings');
function CommandMenuLoader({
name,
search,
hook,
setLoader,
close
}) {
var _hook;
const {
isLoading,
commands = []
} = (_hook = hook({
search
})) !== null && _hook !== void 0 ? _hook : {};
(0, _element.useEffect)(() => {
setLoader(name, isLoading);
}, [setLoader, name, isLoading]);
if (!commands.length) {
return null;
}
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
children: commands.map(command => {
var _command$searchLabel;
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_cmdk.Command.Item, {
value: (_command$searchLabel = command.searchLabel) !== null && _command$searchLabel !== void 0 ? _command$searchLabel : command.label,
onSelect: () => command.callback({
close
}),
id: command.name,
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.__experimentalHStack, {
alignment: "left",
className: (0, _clsx.default)('commands-command-menu__item', {
'has-icon': command.icon
}),
children: [command.icon && /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.Icon, {
icon: command.icon
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.TextHighlight, {
text: command.label,
highlight: search
})
})]
})
}, command.name);
})
});
}
function CommandMenuLoaderWrapper({
hook,
search,
setLoader,
close
}) {
// The "hook" prop is actually a custom React hook
// so to avoid breaking the rules of hooks
// the CommandMenuLoaderWrapper component need to be
// remounted on each hook prop change
// We use the key state to make sure we do that properly.
const currentLoaderRef = (0, _element.useRef)(hook);
const [key, setKey] = (0, _element.useState)(0);
(0, _element.useEffect)(() => {
if (currentLoaderRef.current !== hook) {
currentLoaderRef.current = hook;
setKey(prevKey => prevKey + 1);
}
}, [hook]);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(CommandMenuLoader, {
hook: currentLoaderRef.current,
search: search,
setLoader: setLoader,
close: close
}, key);
}
function CommandMenuGroup({
isContextual,
search,
setLoader,
close
}) {
const {
commands,
loaders
} = (0, _data.useSelect)(select => {
const {
getCommands,
getCommandLoaders
} = select(_store.store);
return {
commands: getCommands(isContextual),
loaders: getCommandLoaders(isContextual)
};
}, [isContextual]);
if (!commands.length && !loaders.length) {
return null;
}
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_cmdk.Command.Group, {
children: [commands.map(command => {
var _command$searchLabel2;
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_cmdk.Command.Item, {
value: (_command$searchLabel2 = command.searchLabel) !== null && _command$searchLabel2 !== void 0 ? _command$searchLabel2 : command.label,
onSelect: () => command.callback({
close
}),
id: command.name,
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.__experimentalHStack, {
alignment: "left",
className: (0, _clsx.default)('commands-command-menu__item', {
'has-icon': command.icon
}),
children: [command.icon && /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.Icon, {
icon: command.icon
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.TextHighlight, {
text: command.label,
highlight: search
})
})]
})
}, command.name);
}), loaders.map(loader => /*#__PURE__*/(0, _jsxRuntime.jsx)(CommandMenuLoaderWrapper, {
hook: loader.hook,
search: search,
setLoader: setLoader,
close: close
}, loader.name))]
});
}
function CommandInput({
isOpen,
search,
setSearch
}) {
const commandMenuInput = (0, _element.useRef)();
const _value = (0, _cmdk.useCommandState)(state => state.value);
const selectedItemId = (0, _element.useMemo)(() => {
const item = document.querySelector(`[cmdk-item=""][data-value="${_value}"]`);
return item?.getAttribute('id');
}, [_value]);
(0, _element.useEffect)(() => {
// Focus the command palette input when mounting the modal.
if (isOpen) {
commandMenuInput.current.focus();
}
}, [isOpen]);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_cmdk.Command.Input, {
ref: commandMenuInput,
value: search,
onValueChange: setSearch,
placeholder: inputLabel,
"aria-activedescendant": selectedItemId,
icon: search
});
}
/**
* @ignore
*/
function CommandMenu() {
const {
registerShortcut
} = (0, _data.useDispatch)(_keyboardShortcuts.store);
const [search, setSearch] = (0, _element.useState)('');
const isOpen = (0, _data.useSelect)(select => select(_store.store).isOpen(), []);
const {
open,
close
} = (0, _data.useDispatch)(_store.store);
const [loaders, setLoaders] = (0, _element.useState)({});
(0, _element.useEffect)(() => {
registerShortcut({
name: 'core/commands',
category: 'global',
description: (0, _i18n.__)('Open the command palette.'),
keyCombination: {
modifier: 'primary',
character: 'k'
}
});
}, [registerShortcut]);
(0, _keyboardShortcuts.useShortcut)('core/commands', /** @type {import('react').KeyboardEventHandler} */
event => {
// Bails to avoid obscuring the effect of the preceding handler(s).
if (event.defaultPrevented) {
return;
}
event.preventDefault();
if (isOpen) {
close();
} else {
open();
}
}, {
bindGlobal: true
});
const setLoader = (0, _element.useCallback)((name, value) => setLoaders(current => ({
...current,
[name]: value
})), []);
const closeAndReset = () => {
setSearch('');
close();
};
if (!isOpen) {
return false;
}
const onKeyDown = event => {
if (
// Ignore keydowns from IMEs
event.nativeEvent.isComposing ||
// Workaround for Mac Safari where the final Enter/Backspace of an IME composition
// is `isComposing=false`, even though it's technically still part of the composition.
// These can only be detected by keyCode.
event.keyCode === 229) {
event.preventDefault();
}
};
const isLoading = Object.values(loaders).some(Boolean);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Modal, {
className: "commands-command-menu",
overlayClassName: "commands-command-menu__overlay",
onRequestClose: closeAndReset,
__experimentalHideHeader: true,
contentLabel: (0, _i18n.__)('Command palette'),
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
className: "commands-command-menu__container",
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_cmdk.Command, {
label: inputLabel,
onKeyDown: onKeyDown,
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
className: "commands-command-menu__header",
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(CommandInput, {
search: search,
setSearch: setSearch,
isOpen: isOpen
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.Icon, {
icon: _icons.search
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_cmdk.Command.List, {
label: (0, _i18n.__)('Command suggestions'),
children: [search && !isLoading && /*#__PURE__*/(0, _jsxRuntime.jsx)(_cmdk.Command.Empty, {
children: (0, _i18n.__)('No results found.')
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(CommandMenuGroup, {
search: search,
setLoader: setLoader,
close: closeAndReset,
isContextual: true
}), search && /*#__PURE__*/(0, _jsxRuntime.jsx)(CommandMenuGroup, {
search: search,
setLoader: setLoader,
close: closeAndReset
})]
})]
})
})
});
}
//# sourceMappingURL=command-menu.js.map
;