import { createSelector } from '@wordpress/data';
import {
displayShortcut,
shortcutAriaLabel,
rawShortcut,
} from '@wordpress/keycodes';
import type { ShortcutKeyCombination } from './actions';
interface ShortcutState {
category: string;
keyCombination: ShortcutKeyCombination;
aliases?: ShortcutKeyCombination[];
description: string;
}
type ShortcutsState = Record< string, ShortcutState >;
/**
* Shared reference to an empty array for cases where it is important to avoid
* returning a new array reference on every invocation.
*/
const EMPTY_ARRAY: ShortcutKeyCombination[] = [];
/**
* Shortcut formatting methods.
*/
const FORMATTING_METHODS = {
/**
* Display formatting.
*/
display: displayShortcut,
/**
* Raw shortcut formatting.
*/
raw: rawShortcut,
/**
* ARIA label formatting.
*/
ariaLabel: shortcutAriaLabel,
};
/**
* Returns a string representing the key combination.
*
* @param shortcut Key combination.
* @param representation Type of representation
* (display, raw, ariaLabel).
*
* @return Shortcut representation.
*/
function getKeyCombinationRepresentation(
shortcut: ShortcutKeyCombination | null,
representation: keyof typeof FORMATTING_METHODS
): string | null {
if ( ! shortcut ) {
return null;
}
return shortcut.modifier
? FORMATTING_METHODS[ representation ][ shortcut.modifier ](
shortcut.character
)
: shortcut.character;
}
/**
* Returns the main key combination for a given shortcut name.
*
* @param {Object} state Global state.
* @param {string} name Shortcut name.
*
* @example
*
*```js
* import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
* import { useSelect } from '@wordpress/data';
* import { createInterpolateElement } from '@wordpress/element';
* import { sprintf } from '@wordpress/i18n';
* const ExampleComponent = () => {
* const {character, modifier} = useSelect(
* ( select ) =>
* select( keyboardShortcutsStore ).getShortcutKeyCombination(
* 'core/editor/next-region'
* ),
* []
* );
*
* return (
*
* { createInterpolateElement(
* sprintf(
* 'Character: %s / Modifier: %s',
* character,
* modifier
* ),
* {
* code: ,
* }
* ) }
*
* );
* };
*```
*
* @return {ShortcutKeyCombination?} Key combination.
*/
export function getShortcutKeyCombination(
state: ShortcutsState,
name: string
): ShortcutKeyCombination | null {
return state[ name ] ? state[ name ].keyCombination : null;
}
/**
* Returns a string representing the main key combination for a given shortcut name.
*
* @param {Object} state Global state.
* @param {string} name Shortcut name.
* @param {keyof FORMATTING_METHODS} representation Type of representation
* (display, raw, ariaLabel).
* @example
*
*```js
* import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
* import { useSelect } from '@wordpress/data';
* import { sprintf } from '@wordpress/i18n';
*
* const ExampleComponent = () => {
* const {display, raw, ariaLabel} = useSelect(
* ( select ) =>{
* return {
* display: select( keyboardShortcutsStore ).getShortcutRepresentation('core/editor/next-region' ),
* raw: select( keyboardShortcutsStore ).getShortcutRepresentation('core/editor/next-region','raw' ),
* ariaLabel: select( keyboardShortcutsStore ).getShortcutRepresentation('core/editor/next-region', 'ariaLabel')
* }
* },
* []
* );
*
* return (
*
* - { sprintf( 'display string: %s', display ) }
* - { sprintf( 'raw string: %s', raw ) }
* - { sprintf( 'ariaLabel string: %s', ariaLabel ) }
*
* );
* };
*```
*
* @return {?string} Shortcut representation.
*/
export function getShortcutRepresentation(
state: ShortcutsState,
name: string,
representation: keyof typeof FORMATTING_METHODS = 'display'
): string | null {
const shortcut = getShortcutKeyCombination( state, name );
return getKeyCombinationRepresentation( shortcut, representation );
}
/**
* Returns the shortcut description given its name.
*
* @param {Object} state Global state.
* @param {string} name Shortcut name.
*
* @example
*
*```js
* import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
* import { useSelect } from '@wordpress/data';
* import { __ } from '@wordpress/i18n';
* const ExampleComponent = () => {
* const shortcutDescription = useSelect(
* ( select ) =>
* select( keyboardShortcutsStore ).getShortcutDescription( 'core/editor/next-region' ),
* []
* );
*
* return shortcutDescription ? (
* { shortcutDescription }
* ) : (
* { __( 'No description.' ) }
* );
* };
*```
* @return {?string} Shortcut description.
*/
export function getShortcutDescription(
state: ShortcutsState,
name: string
): string | null {
return state[ name ] ? state[ name ].description : null;
}
/**
* Returns the aliases for a given shortcut name.
*
* @param {Object} state Global state.
* @param {string} name Shortcut name.
* @example
*
*```js
* import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
* import { useSelect } from '@wordpress/data';
* import { createInterpolateElement } from '@wordpress/element';
* import { sprintf } from '@wordpress/i18n';
* const ExampleComponent = () => {
* const shortcutAliases = useSelect(
* ( select ) =>
* select( keyboardShortcutsStore ).getShortcutAliases(
* 'core/editor/next-region'
* ),
* []
* );
*
* return (
* shortcutAliases.length > 0 && (
*
* { shortcutAliases.map( ( { character, modifier }, index ) => (
* -
* { createInterpolateElement(
* sprintf(
* 'Character:
%s / Modifier: %s',
* character,
* modifier
* ),
* {
* code: ,
* }
* ) }
*
* ) ) }
*
* )
* );
* };
*```
*
* @return {ShortcutKeyCombination[]} Key combinations.
*/
export function getShortcutAliases(
state: ShortcutsState,
name: string
): ShortcutKeyCombination[] {
return state[ name ] && state[ name ].aliases
? state[ name ].aliases
: EMPTY_ARRAY;
}
/**
* Returns the shortcuts that include aliases for a given shortcut name.
*
* @param {Object} state Global state.
* @param {string} name Shortcut name.
* @example
*
*```js
* import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
* import { useSelect } from '@wordpress/data';
* import { createInterpolateElement } from '@wordpress/element';
* import { sprintf } from '@wordpress/i18n';
*
* const ExampleComponent = () => {
* const allShortcutKeyCombinations = useSelect(
* ( select ) =>
* select( keyboardShortcutsStore ).getAllShortcutKeyCombinations(
* 'core/editor/next-region'
* ),
* []
* );
*
* return (
* allShortcutKeyCombinations.length > 0 && (
*
* { allShortcutKeyCombinations.map(
* ( { character, modifier }, index ) => (
* -
* { createInterpolateElement(
* sprintf(
* 'Character:
%s / Modifier: %s',
* character,
* modifier
* ),
* {
* code: ,
* }
* ) }
*
* )
* ) }
*
* )
* );
* };
*```
*
* @return {ShortcutKeyCombination[]} Key combinations.
*/
export const getAllShortcutKeyCombinations = createSelector(
( state, name ) => {
return [
getShortcutKeyCombination( state, name ),
...getShortcutAliases( state, name ),
].filter(
( combination ): combination is ShortcutKeyCombination =>
!! combination
);
},
( state, name ) => [ state[ name ] ]
);
/**
* Returns the raw representation of all the keyboard combinations of a given shortcut name.
*
* @param {Object} state Global state.
* @param {string} name Shortcut name.
*
* @example
*
*```js
* import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
* import { useSelect } from '@wordpress/data';
* import { createInterpolateElement } from '@wordpress/element';
* import { sprintf } from '@wordpress/i18n';
*
* const ExampleComponent = () => {
* const allShortcutRawKeyCombinations = useSelect(
* ( select ) =>
* select( keyboardShortcutsStore ).getAllShortcutRawKeyCombinations(
* 'core/editor/next-region'
* ),
* []
* );
*
* return (
* allShortcutRawKeyCombinations.length > 0 && (
*
* { allShortcutRawKeyCombinations.map(
* ( shortcutRawKeyCombination, index ) => (
* -
* { createInterpolateElement(
* sprintf(
* '
%s',
* shortcutRawKeyCombination
* ),
* {
* code: ,
* }
* ) }
*
* )
* ) }
*
* )
* );
* };
*```
*
* @return {string[]} Shortcuts.
*/
export const getAllShortcutRawKeyCombinations = createSelector(
( state, name ) => {
return getAllShortcutKeyCombinations( state, name ).map(
( combination ) =>
getKeyCombinationRepresentation( combination, 'raw' )
);
},
( state, name ) => [ state[ name ] ]
);
/**
* Returns the shortcut names list for a given category name.
*
* @param {Object} state Global state.
* @param {string} name Category name.
* @example
*
*```js
* import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
* import { useSelect } from '@wordpress/data';
*
* const ExampleComponent = () => {
* const categoryShortcuts = useSelect(
* ( select ) =>
* select( keyboardShortcutsStore ).getCategoryShortcuts(
* 'block'
* ),
* []
* );
*
* return (
* categoryShortcuts.length > 0 && (
*
* { categoryShortcuts.map( ( categoryShortcut ) => (
* - { categoryShortcut }
* ) ) }
*
* )
* );
* };
*```
* @return {string[]} Shortcut names.
*/
export const getCategoryShortcuts = createSelector(
( state: ShortcutsState, categoryName: string ): string[] => {
return Object.entries< ShortcutState >( state )
.filter( ( [ , shortcut ] ) => shortcut.category === categoryName )
.map( ( [ name ] ) => name );
},
( state: ShortcutsState ) => [ state ]
);