> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-docs-audit-content-webhooks.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# ShortCut Formatter

> ShortCut Formatter — CometChat integration guide.

## Overview

The `ShortCutFormatter` class extends the [CometChatTextFormatter](/ui-kit/react/v4/custom-text-formatter-guide) class to provide a mechanism for handling shortcuts within messages. This guide will walk you through the process of using `ShortCutFormatter` to implement shortcut extensions in your CometChat application.

<Frame>
  <img src="https://mintcdn.com/cometchat-22654f5b-docs-audit-content-webhooks/CPpOwQ6tJe7YNAEX/images/e2224d89-shortcutformatter_overview_web_screens-5e976b11936b33a6d544a03ad53842bc.png?fit=max&auto=format&n=CPpOwQ6tJe7YNAEX&q=85&s=97596cd5df1b2afd181019a75e78ccdf" width="3600" height="2400" data-path="images/e2224d89-shortcutformatter_overview_web_screens-5e976b11936b33a6d544a03ad53842bc.png" />
</Frame>

## Usage

Here are the steps to create a `ShortCutFormatter` for CometChat using the CometChatTextFormatter:

1. Firstly, you need to import `CometChatTextFormatter` from the CometChat UI Kit react library.

```javascript theme={null}
import { CometChatTextFormatter } from "@cometchat/chat-uikit-react";
```

2. Now, extend the `CometChatTextFormatter` class to create your custom text formatter class. In this case, let's create a `HashTagTextFormatter`.

```javascript theme={null}
class ShortCutFormatter extends CometChatTextFormatter {
  ...
}
```

3. Set up the `trackCharacter` and define any necessary private fields.

```javascript theme={null}
this.setTrackingCharacter("!");
```

4. Implement the `onKeyDown` method to handle key events and trigger actions based on defined shortcuts.

```javascript theme={null}
onKeyDown(event: KeyboardEvent) {
  // Your implementation
}
```

5. Implement any additional methods required for your custom formatter, such as opening and closing dialogs, handling button clicks, and formatting text.

```javascript theme={null}
openDialog(buttonText: string) {
  // Your implementation
}

closeDialog() {
  // Your implementation
}

handleButtonClick = () => {
  // Your implementation
};

getFormattedText(text: string): string {
  return text;
}

private getTextBeforeCaret(caretPosition: number): string {
  // Your implementation
}
```

## Example

Below is an example demonstrating how to use a custom formatter class in components such as [CometChatConversations](/ui-kit/react/v4/conversations), [CometChatMessageList](/ui-kit/react/v4/message-list), [CometChatMessageComposer](/ui-kit/react/v4/message-composer).

<Frame>
  <img src="https://mintcdn.com/cometchat-22654f5b-docs-audit-content-webhooks/qwv2C0wM1Djl_VYB/images/fb445527-shortcutformatter-4818505361f333ec7728b029e9b2da54.png?fit=max&auto=format&n=qwv2C0wM1Djl_VYB&q=85&s=29fddab227dfddd9c6881c6577f99ef6" width="1800" height="1200" data-path="images/fb445527-shortcutformatter-4818505361f333ec7728b029e9b2da54.png" />
</Frame>

<Frame>
  <img src="https://mintcdn.com/cometchat-22654f5b-docs-audit-content-webhooks/tp6xSR_fOiM1f2LY/images/ad81ba1c-shortcutformatter_click_web_screens-253bff958c9b63d7d171d2adcd5a6673.png?fit=max&auto=format&n=tp6xSR_fOiM1f2LY&q=85&s=81540a012df02a98011576a3bf6f7ce4" width="3600" height="2400" data-path="images/ad81ba1c-shortcutformatter_click_web_screens-253bff958c9b63d7d171d2adcd5a6673.png" />
</Frame>

<Tabs>
  <Tab title="ShortCutFormatter.ts">
    ```typescript theme={null}
    import { CometChatTextFormatter } from "@cometchat/chat-uikit-react";
    import DialogHelper from "./Dialog";
    import { CometChat } from "@cometchat/chat-sdk-javascript";

    class ShortcutFormatter extends CometChatTextFormatter {
      private shortcuts: { [key: string]: string } = {};
      private dialogIsOpen: boolean = false;
      private dialogHelper = new DialogHelper();
      private currentShortcut: string | null = null; // Track the currently open shortcut

      constructor() {
        super();
        this.setTrackingCharacter("!");
        CometChat.callExtension("message-shortcuts", "GET", "v1/fetch", undefined)
          .then((data: any) => {
            if (data && data.shortcuts) {
              this.shortcuts = data.shortcuts;
            }
          })
          .catch((error) => console.log("error fetching shortcuts", error));
      }

      onKeyDown(event: KeyboardEvent) {
        const caretPosition =
          this.currentCaretPosition instanceof Selection
            ? this.currentCaretPosition.anchorOffset
            : 0;
        const textBeforeCaret = this.getTextBeforeCaret(caretPosition);

        const match = textBeforeCaret.match(/!([a-zA-Z]+)$/);
        if (match) {
          const shortcut = match[0];
          const replacement = this.shortcuts[shortcut];
          if (replacement) {
            // Close the currently open dialog, if any
            if (this.dialogIsOpen && this.currentShortcut !== shortcut) {
              this.closeDialog();
            }
            this.openDialog(replacement, shortcut);
          }
        }
      }

      getCaretPosition() {
        if (!this.currentCaretPosition?.rangeCount) return { x: 0, y: 0 };
        const range = this.currentCaretPosition?.getRangeAt(0);
        const rect = range.getBoundingClientRect();
        return {
          x: rect.left,
          y: rect.top,
        };
      }

      openDialog(buttonText: string, shortcut: string) {
        this.dialogHelper.createDialog(
          () => this.handleButtonClick(buttonText),
          buttonText
        );
        this.dialogIsOpen = true;
        this.currentShortcut = shortcut;
      }

      closeDialog() {
        this.dialogHelper.closeDialog(); // Use DialogHelper to close the dialog
        this.dialogIsOpen = false;
        this.currentShortcut = null;
      }

      handleButtonClick = (buttonText: string) => {
        if (this.currentCaretPosition && this.currentRange) {
          // Inserting the replacement text corresponding to the shortcut
          const shortcut = Object.keys(this.shortcuts).find(
            (key) => this.shortcuts[key] === buttonText
          );
          if (shortcut) {
            const replacement = this.shortcuts[shortcut];
            this.addAtCaretPosition(
              replacement,
              this.currentCaretPosition,
              this.currentRange
            );
          }
        }
        if (this.dialogIsOpen) {
          this.closeDialog();
        }
      };

      getFormattedText(text: string): string {
        return text;
      }

      private getTextBeforeCaret(caretPosition: number): string {
        if (
          this.currentRange &&
          this.currentRange.startContainer &&
          typeof this.currentRange.startContainer.textContent === "string"
        ) {
          const textContent = this.currentRange.startContainer.textContent;
          if (textContent.length >= caretPosition) {
            return textContent.substring(0, caretPosition);
          }
        }
        return "";
      }
    }

    export default ShortcutFormatter;
    ```
  </Tab>

  <Tab title="Dialog.tsx">
    ```typescript theme={null}
    import React from "react";
    import ReactDOM from "react-dom";

    interface DialogProps {
      onClick: () => void;
      buttonText: string;
    }

    const Dialog: React.FC<DialogProps> = ({ onClick, buttonText }) => {
      console.log("buttonText", buttonText);

      return (
        <div
          style={{
            position: "fixed",
            left: "300px",
            top: "664px",
            width: "800px",
            height: "45px",
          }}
        >
          <button
            style={{
              width: "800px",
              height: "100%",
              cursor: "pointer",
              backgroundColor: "#f2e6ff",
              border: "2px solid #9b42f5",
              borderRadius: "12px",
              textAlign: "left",
              paddingLeft: "20px",
              font: "600 15px sans-serif, Inter",
            }}
            onClick={onClick}
          >
            {buttonText}
          </button>
        </div>
      );
    };

    export default class DialogHelper {
      private dialogContainer: HTMLDivElement | null = null;

      createDialog(onClick: () => void, buttonText: string) {
        this.dialogContainer = document.createElement("div");
        document.body.appendChild(this.dialogContainer);

        ReactDOM.render(
          <Dialog onClick={onClick} buttonText={buttonText} />,
          this.dialogContainer
        );
      }

      closeDialog() {
        if (this.dialogContainer) {
          ReactDOM.unmountComponentAtNode(this.dialogContainer);
          this.dialogContainer.remove();
          this.dialogContainer = null;
        }
      }
    }
    ```
  </Tab>

  <Tab title="ConversationDemo.tsx">
    ```typescript theme={null}
    import ShortcutFormatter from "./ShortCutFormatter";

    <CometChatConversations textFormatters={[new ShortcutFormatter()]} />;
    ```
  </Tab>
</Tabs>

***
