remirror/remirror

View on GitHub
packages/remirror__react-components/src/react-component-utils.ts

Summary

Maintainability
A
0 mins
Test Coverage
import {
  capitalize,
  CommandDecoratorMessageProps,
  CommandDecoratorShortcut,
  CommandDecoratorValue,
  getShortcutSymbols,
  includes,
  isArray,
  isEqual,
  isFunction,
  isString,
  ProsemirrorAttributes,
} from '@remirror/core';
import type { I18n } from '@remirror/i18n';

/**
 * Get the value from the option passed into the command.
 */

export function getCommandOptionValue<Type>(
  value: CommandDecoratorValue<Type> | undefined,
  commandProps: CommandDecoratorMessageProps,
): Type | undefined {
  return isFunction(value) ? value(commandProps) : value;
}

/**
 * Checks whether the first element in an array is a string and assumes the
 * whole array is a string array.
 */
function isStringArray(array: unknown[]): array is string[] {
  return isString(array[0]);
}
/**
 * Get the string value from the available UI Shortcut.
 */

export function getUiShortcutString(
  uiShortcut: CommandDecoratorShortcut,
  attrs: ProsemirrorAttributes,
): string {
  if (isString(uiShortcut)) {
    return uiShortcut;
  }

  if (!isArray(uiShortcut)) {
    return uiShortcut.shortcut;
  }

  if (isStringArray(uiShortcut)) {
    return uiShortcut[0] ?? '';
  }

  return (
    (uiShortcut.find((shortcut) => isEqual(shortcut.attrs, attrs)) ?? uiShortcut[0])?.shortcut ?? ''
  );
}

/**
 * How to display the symbols.
 */
type Modifiers = 'shift' | 'command' | 'control' | 'alt';

interface ShortcutStringOptions {
  /**
   * True to display all named values as symbols (where possible, otherwise
   * display the translated string).
   *
   * @defaultValue false
   */
  namedAsSymbol?: boolean | string[];

  /**
   * `true` to display modifiers as symbols. `false` to display as translated strings.
   *
   * An array to only set the provided array values as symbols.
   *
   * @defaultValue true
   */
  modifierAsSymbol?: boolean | Modifiers[];

  /**
   * How the values should be cased.
   *
   * @defaultValue 'title'
   */
  casing?: keyof typeof CASINGS;

  /**
   * The separator to use between symbols.
   *
   * @defaultValue ' '
   */
  separator?: string;

  /**
   * A translation utility for translating a predefined string / or message
   * descriptor.
   */
  t: I18n['_'];
}

const CASINGS = {
  title: (value: string) => capitalize(value),
  upper: (value: string) => value.toLocaleUpperCase(),
  lower: (value: string) => value.toLocaleLowerCase(),
};

/**
 * Get a normalized shortcut as a string.
 */
export function getShortcutString(shortcut: string, options: ShortcutStringOptions): string {
  const {
    casing = 'title',
    namedAsSymbol = false,
    modifierAsSymbol = true,
    separator = ' ',
    t,
  } = options;

  const symbols = getShortcutSymbols(shortcut);
  const stringSymbols: string[] = [];

  const transform = CASINGS[casing];

  for (const sym of symbols) {
    if (sym.type === 'char') {
      stringSymbols.push(transform(sym.key));
      continue;
    }

    if (sym.type === 'named') {
      const value =
        namedAsSymbol === true || (isArray(namedAsSymbol) && includes(namedAsSymbol, sym.key))
          ? sym.symbol ?? t(sym.i18n)
          : t(sym.i18n);
      stringSymbols.push(transform(value));

      continue;
    }

    const value =
      modifierAsSymbol === true ||
      (isArray(modifierAsSymbol) && includes(modifierAsSymbol, sym.key))
        ? sym.symbol
        : t(sym.i18n);
    stringSymbols.push(transform(value));
  }

  return stringSymbols.join(separator);
}