@fidurcode/chat
v1.0.0
Published
An Angular library providing a ready-made Messenger-style chat widget — a floating FAB button, a conversation list panel, and chat windows docked to a bottom bar.
Downloads
1,412
Readme
@fidurcode/chat
An Angular library providing a ready-made Messenger-style chat widget — a floating FAB button, a conversation list panel, and chat windows docked to a bottom bar.
The library handles only the visual and UX layer. All business logic (WebSocket, conversation list, messages, typing events) is provided externally by implementing ChatContract.
Requirements
- Angular
^21.2.0 - Angular Material
^21.2.0
Installation
npm install @fidurcode/chatQuick start
1. Implement ChatContract
Create a service that implements the ChatContract interface:
import { Injectable, signal } from '@angular/core';
import { ChatContract, ChatConversation, ChatMessage } from '@fidurcode/chat';
@Injectable({ providedIn: 'root' })
export class MyChatService implements ChatContract {
readonly conversations = signal<ChatConversation[]>([]);
readonly unreadCount = signal(0);
connect(userId: string | number): void {
// open WebSocket connection
// when an incoming "typing" event arrives from the server, set isTyping: true
// on the matching conversation in this.conversations
}
disconnect(): void {
// close connection
}
getMessages(conversationId: string) {
return signal<ChatMessage[]>([]);
}
sendMessage(conversationId: string, content: string): void {
// send message over WebSocket
}
markConversationAsRead(conversationId: string): void {
// mark conversation as read
}
notifyTyping(conversationId: string): void {
// send "typing" event to the server for this conversation
// e.g.: this.socket.emit('typing', { conversationId })
}
notifyStopTyping(conversationId: string): void {
// send "stop_typing" event to the server for this conversation
// e.g.: this.socket.emit('stop_typing', { conversationId })
}
}2. Provide the service and add the widget
In your module or providers configuration:
import { CHAT_SERVICE } from '@fidurcode/chat';
import { MyChatService } from './my-chat.service';
providers: [
{ provide: CHAT_SERVICE, useExisting: MyChatService }
]In your template:
<fidurcode-chat-button [config]="chatConfig" />import { ChatConfig } from '@fidurcode/chat';
chatConfig: ChatConfig = {
currentUserId: 'user-123',
};3. Import the module
import { ChatModule } from '@fidurcode/chat';
@NgModule({
imports: [ChatModule],
})Configuration (ChatConfig)
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| currentUserId | string / number | — | Required. ID of the logged-in user |
| position | 'bottom-right' / 'bottom-left' | 'bottom-right' | Widget position |
| bottom | number | 20 | Distance from bottom edge (px) |
| side | number | 20 | Distance from side edge (px) |
| maxOpenWindows | number | 3 | Maximum number of simultaneously open windows |
| buttonIcon | string | 'chat' | Material Icons icon name for the FAB |
| labels | ChatLabels | — | Override text labels |
Labels (labels)
labels: {
title?: string; // default: 'Wiadomości'
searchPlaceholder?: string; // default: 'Szukaj'
inputPlaceholder?: string; // default: 'Aa'
emptyConversations?: string; // default: 'Brak rozmów'
emptyMessages?: string; // default: 'Napisz pierwszą wiadomość'
online?: string; // default: 'Aktywny/a'
}Data interfaces
ChatConversation
interface ChatConversation {
id: string;
name: string;
avatarUrl?: string;
lastMessage?: string;
lastMessageAt?: string; // ISO 8601
unreadCount: number;
isOnline?: boolean;
isTyping?: boolean; // when true, shows an animated ⋯ typing bubble in the chat window
participants?: ChatParticipant[];
}ChatMessage
interface ChatMessage {
id: string;
conversationId: string;
senderId: string | number;
content: string;
sentAt: string; // ISO 8601
status?: 'sending' | 'sent' | 'delivered' | 'read';
}ChatContract
interface ChatContract {
conversations: Signal<ChatConversation[]>;
unreadCount: Signal<number>;
connect(userId: string | number): void;
disconnect(): void;
getMessages(conversationId: string): Signal<ChatMessage[]>;
sendMessage(conversationId: string, content: string): void;
markConversationAsRead(conversationId: string): void;
notifyTyping(conversationId: string): void;
notifyStopTyping(conversationId: string): void;
}Typing indicator
The library has a full typing indicator flow. Here is how it works end-to-end:
What the library does automatically
- While the user types in a chat window,
ChatWindowComponentemits a(typing)output after a 300 ms debounce. - After 1500 ms of inactivity (or immediately on send), it emits
(stopTyping). - When
conversation.isTyping === true, the chat window renders an animated bubble with three bouncing dots at the bottom of the message list.
What the host app must do
Step 1 — wire up the outputs in whichever component renders fidurcode-chat-window:
<fidurcode-chat-window
[conversation]="conv"
[messages]="messages"
[config]="chatConfig"
(typing)="chatUIService.notifyTyping($event.conversationId)"
(stopTyping)="chatUIService.notifyStopTyping($event.conversationId)"
(messageSent)="chatUIService.sendMessage($event.conversationId, $event.content)"
(minimize)="chatUIService.toggleMinimize(conv.id)"
(close)="chatUIService.closeConversation(conv.id)"
/>Step 2 — implement notifyTyping / notifyStopTyping in your ChatContract service to send the event to the server (e.g., via WebSocket or HTTP).
Step 3 — handle incoming typing events from the server. When person B receives a typing notification from person A, update person A's conversation in the conversations signal:
// inside connect() — handling incoming WebSocket messages
socket.on('typing', ({ conversationId }) => {
this.conversations.update(list =>
list.map(c => c.id === conversationId ? { ...c, isTyping: true } : c)
);
});
socket.on('stop_typing', ({ conversationId }) => {
this.conversations.update(list =>
list.map(c => c.id === conversationId ? { ...c, isTyping: false } : c)
);
});Features
FAB button
- Round button anchored to a corner of the viewport (
position: fixed) - Animated unread count badge (color:
mat-sys-primary) - Color driven by CSS variables or the app's Material Design theme
Conversation panel
- List of conversations with avatars and initials as fallback
- Real-time search with filtering
- Green online indicator with animated pulse
- Unread conversations shown in bold with a counter badge
- Relative timestamps (
now,5m,3h,DD/MM)
Chat windows
- Messenger-style — windows dock to the bottom bar, up to
maxOpenWindowsat once - Message grouping — consecutive messages from the same sender share flattened corners and tighter spacing; avatar appears only on the last message in a group
- Typing indicator — animated
⋯dots whenisTyping = trueon a conversation - Badge on minimized window — unread count visible without expanding
- Message statuses:
schedule(sending),done(sent),done_all(delivered / read) - Auto-scroll to the latest message
- Send on Enter or the send button (disabled when input is empty)
Keyboard shortcuts
| Shortcut | Action |
|----------|--------|
| Escape | Close the conversation panel |
Theming — CSS Custom Properties
The widget inherits colors from the app's theme via Material Design tokens. Override any aspect with CSS custom properties:
fidurcode-chat-button {
/* Panel and window backgrounds */
--fidurcode-chat-bg: var(--mat-sys-surface);
--fidurcode-chat-color: var(--mat-sys-on-surface);
/* FAB button */
--fidurcode-chat-fab-bg: var(--mat-sys-primary);
--fidurcode-chat-fab-color: var(--mat-sys-on-primary);
/* Unread badge on the FAB */
--fidurcode-chat-fab-badge-bg: var(--mat-sys-primary);
--fidurcode-chat-fab-badge-color: var(--mat-sys-on-primary);
/* Avatars */
--fidurcode-chat-avatar-bg: var(--mat-sys-primary);
--fidurcode-chat-avatar-color: var(--mat-sys-on-primary);
/* Unread badges in the panel */
--fidurcode-chat-badge-bg: var(--mat-sys-primary);
--fidurcode-chat-badge-color: var(--mat-sys-on-primary);
/* Message bubbles */
--fidurcode-chat-bubble-other-bg: var(--mat-sys-tertiary);
--fidurcode-chat-bubble-other-color: var(--mat-sys-on-tertiary);
--fidurcode-chat-bubble-own-bg: var(--mat-sys-primary);
--fidurcode-chat-bubble-own-color: var(--mat-sys-on-primary);
/* Online indicator */
--fidurcode-chat-online-color: #31a24c;
--fidurcode-chat-online-ring: var(--fidurcode-chat-bg);
/* Dimensions (optional) */
--fidurcode-chat-panel-width: 320px;
--fidurcode-chat-panel-max-height: 520px;
--fidurcode-chat-window-width: 300px;
--fidurcode-chat-window-body-height: 360px;
}Building
ng build chatBuild artifacts are placed in dist/chat/.
