ShortCut Formatter
Overview
The ShortCutFormatter class extends the CometChatTextFormatter 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.

Usage
Here are the steps to create a ShortCutFormatter for CometChat using the CometChatTextFormatter:
- Firstly, you need to import
CometChatTextFormatterfrom the CometChat UI Kit angular library.
import { CometChatTextFormatter } from "@cometchat/chat-uikit-angular";
- Now, extend the
CometChatTextFormatterclass to create your custom text formatter class. In this case, let's create aHashTagTextFormatter.
class ShortCutFormatter extends CometChatTextFormatter {
...
}
- Set up the
trackCharacterand define any necessary private fields.
this.setTrackingCharacter("!");
- Implement the
onKeyDownmethod to handle key events and trigger actions based on defined shortcuts.
onKeyDown(event: KeyboardEvent) {
// Your implementation
}
- Implement any additional methods required for your custom formatter, such as opening and closing dialogs, handling button clicks, and formatting text.
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, CometChatMessageList, CometChatMessageComposer.


- ShortCutFormatter.ts
- app.component.ts
- app.component.html
import { CometChatTextFormatter } from "@cometchat/uikit-shared";
import { Subject } from 'rxjs';
import { CometChat } from "@cometchat/chat-sdk-javascript";
export class ShortcutFormatter extends CometChatTextFormatter {
private shortcuts: { [key: string]: string } = {};
private dialogIsOpen: boolean = false;
private currentShortcut: string | null = null;
private openDialogSubject = new Subject<{ closeDialog?: boolean, buttonText?: string, shortcut?: string, handleButtonClick?:any }>();
openDialog$ = this.openDialogSubject.asObservable();
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));
}
override 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) {
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.openDialogSubject.next({ buttonText, shortcut , handleButtonClick: this.handleButtonClick});
this.dialogIsOpen = true;
this.currentShortcut = shortcut;
}
closeDialog() {
this.openDialogSubject.next({closeDialog: true});
}
handleButtonClick = (buttonText: string) => {
console.log(buttonText);
if (this.currentCaretPosition && this.currentRange) {
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();
}
};
override 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;
import { CometChat } from '@cometchat/chat-sdk-javascript';
import { Component, OnInit } from '@angular/core';
import { CometChatThemeService, CometChatUIKit } from '@cometchat/chat-uikit-angular';
import "@cometchat/uikit-elements";
import { ShortcutFormatter } from '../ShortCutFormatter';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
dialogIsOpen: boolean = false;
currentDialogButtonText: string = '';
private dialogSubscription!: Subscription;
onClick : any;
ngOnInit(): void {
CometChat.getUser("uid").then((user:CometChat.User)=>{
this.userObject=user;
});
}
public userObject!: CometChat.User;
constructor(private themeService:CometChatThemeService) {
themeService.theme.palette.setMode("light")
themeService.theme.palette.setPrimary({ light: "#6851D6", dark: "#6851D6" })
this.dialogSubscription = this.shortcutFormatter.openDialog$.subscribe(
({ closeDialog, buttonText, shortcut, handleButtonClick }) => {
if(closeDialog) {
this.closeDialog();
return;
}
this.onOpenDialog(buttonText!, shortcut!, handleButtonClick!)
}
);
}
ngOnDestroy() {
this.dialogSubscription.unsubscribe();
}
onOpenDialog(buttonText: string, shortcut: string, handleButtonClick: any) {
this.currentDialogButtonText = buttonText;
this.dialogIsOpen = true;
this.onClick= handleButtonClick;
}
closeDialog() {
this.dialogIsOpen = false;
this.currentDialogButtonText = '';
}
onLogin(UID?: any) {
CometChatUIKit.login({ uid: UID }).then(
(user) => {
setTimeout(() => {
window.location.reload();
}, 1000);
},
(error) => {
console.log("Login failed with exception:", { error });
}
);
}
}
<div class="fullwidth">
<cometchat-message-composer
*ngIf="userObject"
[user]="userObject"
[textFormatters]="[shortcutFormatter]"
></cometchat-message-composer>
</div>
<div *ngIf="dialogIsOpen" [ngStyle]="{ position: 'absolute', top: '150px', left: '50%', transform: 'translateX(-50%)', width: '600px' }">
<button (click)="onClick(currentDialogButtonText)" [ngStyle]="{ width: '100%', height: '100%', cursor: 'pointer', backgroundColor: '#f2e6ff', border: '2px solid #9b42f5', borderRadius: '12px', textAlign: 'left', font: '600 15px sans-serif, Inter' }">
{{ currentDialogButtonText }}
</button>
</div>