The CometChatCollaborativeDocumentBubble component renders collaborative document messages within chat. It displays a banner image, title, subtitle, and an action button that opens the document in a new fullscreen window.
Overview
The Collaborative Document Bubble component processes CometChat.CustomMessage objects of type extension_document to display collaborative document content:
- Message Object Processing: Extracts document URL from message metadata
- Theme-aware Banner Images: Automatically selects light/dark banner images based on current theme
- Localized Text: Displays title, subtitle, and button text using localization keys
- Sender/Receiver Styling: Supports outgoing (right-aligned) and incoming (left-aligned) visual variants
- Action Button: Opens the document URL in a new fullscreen window
- Accessibility: Full keyboard navigation and screen reader support
Basic Usage
Simple Document Message
import { Component } from '@angular/core';
import { CometChat } from '@cometchat/chat-sdk-javascript';
import {
CometChatCollaborativeDocumentBubbleComponent,
MessageBubbleAlignment
} from '@cometchat/chat-uikit-angular';
@Component({
selector: 'app-document-message',
standalone: true,
imports: [CometChatCollaborativeDocumentBubbleComponent],
template: `
<cometchat-collaborative-document-bubble
[message]="documentMessage"
[alignment]="MessageBubbleAlignment.LEFT"
></cometchat-collaborative-document-bubble>
`
})
export class DocumentMessageComponent {
documentMessage!: CometChat.CustomMessage;
MessageBubbleAlignment = MessageBubbleAlignment;
}
Incoming vs Outgoing Messages
import { Component } from '@angular/core';
import { CometChat } from '@cometchat/chat-sdk-javascript';
import {
CometChatCollaborativeDocumentBubbleComponent,
MessageBubbleAlignment
} from '@cometchat/chat-uikit-angular';
@Component({
selector: 'app-message-list',
standalone: true,
imports: [CometChatCollaborativeDocumentBubbleComponent],
template: `
<!-- Incoming message (left-aligned) -->
<cometchat-collaborative-document-bubble
[message]="incomingMessage"
[alignment]="MessageBubbleAlignment.LEFT"
></cometchat-collaborative-document-bubble>
<!-- Outgoing message (right-aligned) -->
<cometchat-collaborative-document-bubble
[message]="outgoingMessage"
[alignment]="MessageBubbleAlignment.RIGHT"
></cometchat-collaborative-document-bubble>
`
})
export class MessageListComponent {
incomingMessage!: CometChat.CustomMessage;
outgoingMessage!: CometChat.CustomMessage;
MessageBubbleAlignment = MessageBubbleAlignment;
}
With Event Handler
import { Component } from '@angular/core';
import { CometChat } from '@cometchat/chat-sdk-javascript';
import { CometChatCollaborativeDocumentBubbleComponent } from '@cometchat/chat-uikit-angular';
@Component({
selector: 'app-interactive-document',
standalone: true,
imports: [CometChatCollaborativeDocumentBubbleComponent],
template: `
<cometchat-collaborative-document-bubble
[message]="documentMessage"
(buttonClick)="onDocumentOpen($event)"
></cometchat-collaborative-document-bubble>
`
})
export class InteractiveDocumentComponent {
documentMessage!: CometChat.CustomMessage;
onDocumentOpen(url: string): void {
console.log('Document opened:', url);
// Custom handling - the component already opens the URL in a new window
}
}
Properties
| Property | Type | Default | Description |
|---|
message | CometChat.CustomMessage | required | The CometChat.CustomMessage object containing collaborative document metadata |
alignment | MessageBubbleAlignment | MessageBubbleAlignment.LEFT | The alignment of the message bubble. LEFT for incoming/receiver messages, RIGHT for outgoing/sender messages |
disableInteraction | boolean | false | When true, disables the action button and other interactive elements within the bubble |
Events
| Event | Payload Type | Description |
|---|
buttonClick | string | Emitted when the action button is clicked. Contains the document URL string |
The component extracts the document URL from the following metadata path:
{
"@injected": {
"extensions": {
"document": {
"document_url": "https://example.com/document/123"
}
}
}
}
Advanced Usage
Complete Chat Message Component
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CometChat } from '@cometchat/chat-sdk-javascript';
import {
CometChatCollaborativeDocumentBubbleComponent,
MessageBubbleAlignment
} from '@cometchat/chat-uikit-angular';
@Component({
selector: 'app-document-chat-message',
standalone: true,
imports: [CommonModule, CometChatCollaborativeDocumentBubbleComponent],
template: `
<div class="message-container" [class.outgoing]="isOutgoing">
<div class="message-bubble">
<cometchat-collaborative-document-bubble
[message]="message"
[alignment]="alignment"
(buttonClick)="handleDocumentOpen($event)"
></cometchat-collaborative-document-bubble>
</div>
<div class="message-metadata">
<span class="timestamp">{{ message.getSentAt() * 1000 | date:'short' }}</span>
</div>
</div>
`,
styles: [`
.message-container {
display: flex;
flex-direction: column;
margin-bottom: 12px;
max-width: 70%;
}
.message-container.outgoing {
align-self: flex-end;
align-items: flex-end;
}
.message-bubble {
display: inline-block;
}
.message-metadata {
display: flex;
align-items: center;
gap: 6px;
margin-top: 4px;
font-size: 11px;
color: #999;
}
`]
})
export class DocumentChatMessageComponent {
@Input() message!: CometChat.CustomMessage;
@Input() loggedInUserId!: string;
get isOutgoing(): boolean {
return this.message.getSender().getUid() === this.loggedInUserId;
}
get alignment(): MessageBubbleAlignment {
return this.isOutgoing ? MessageBubbleAlignment.RIGHT : MessageBubbleAlignment.LEFT;
}
handleDocumentOpen(url: string): void {
console.log('Document URL:', url);
// Additional tracking or custom handling
}
}
Message List with Document Bubbles
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CometChat } from '@cometchat/chat-sdk-javascript';
import {
CometChatCollaborativeDocumentBubbleComponent,
MessageBubbleAlignment
} from '@cometchat/chat-uikit-angular';
@Component({
selector: 'app-document-message-list',
standalone: true,
imports: [CommonModule, CometChatCollaborativeDocumentBubbleComponent],
template: `
<div class="message-list">
@for (message of messages; track message.getId()) {
<div class="message-wrapper" [class.outgoing]="isOutgoing(message)">
<cometchat-collaborative-document-bubble
[message]="message"
[alignment]="getAlignment(message)"
(buttonClick)="onDocumentOpen($event)"
></cometchat-collaborative-document-bubble>
</div>
}
</div>
`,
styles: [`
.message-list {
display: flex;
flex-direction: column;
gap: 8px;
padding: 16px;
}
.message-wrapper {
display: flex;
max-width: 70%;
}
.message-wrapper.outgoing {
align-self: flex-end;
}
`]
})
export class DocumentMessageListComponent implements OnInit {
messages: CometChat.CustomMessage[] = [];
loggedInUser!: CometChat.User;
async ngOnInit(): Promise<void> {
this.loggedInUser = await CometChat.getLoggedinUser();
}
isOutgoing(message: CometChat.CustomMessage): boolean {
return message.getSender().getUid() === this.loggedInUser?.getUid();
}
getAlignment(message: CometChat.CustomMessage): MessageBubbleAlignment {
return this.isOutgoing(message)
? MessageBubbleAlignment.RIGHT
: MessageBubbleAlignment.LEFT;
}
onDocumentOpen(url: string): void {
console.log('Opening document:', url);
}
}
Customization
Styling with CSS Variables
The Collaborative Document Bubble component uses CSS variables exclusively for easy customization:
/* Custom document bubble styling */
cometchat-collaborative-document-bubble {
/* Spacing */
--cometchat-spacing-1: 4px;
--cometchat-spacing-2: 8px;
--cometchat-spacing-3: 12px;
--cometchat-spacing-4: 16px;
/* Typography */
--cometchat-font-heading4-medium: 500 16px 'Inter';
--cometchat-font-body-regular: 400 14px 'Inter';
--cometchat-font-button-medium: 500 14px 'Inter';
/* Colors - Incoming messages */
--cometchat-background-color-01: #FFFFFF;
--cometchat-background-color-02: #F5F5F5;
--cometchat-text-color-primary: #141414;
--cometchat-text-color-secondary: #666666;
/* Colors - Outgoing messages */
--cometchat-primary-button-background: #6852D6;
--cometchat-primary-button-text: #FFFFFF;
/* Border radius */
--cometchat-radius-2: 8px;
--cometchat-radius-3: 12px;
/* Border colors */
--cometchat-border-color-light: #E0E0E0;
}
Available CSS Variables
| Variable | Purpose | Default |
|---|
--cometchat-spacing-1 to --cometchat-spacing-4 | Padding, margin, gap | 4px to 16px |
--cometchat-font-heading4-medium | Title font | 500 16px Roboto |
--cometchat-font-body-regular | Subtitle font | 400 14px Roboto |
--cometchat-font-button-medium | Button text font | 500 14px Roboto |
--cometchat-background-color-01 | Bubble background | #FFFFFF |
--cometchat-background-color-02 | Body section background | #F5F5F5 |
--cometchat-text-color-primary | Title color | #141414 |
--cometchat-text-color-secondary | Subtitle color | #666666 |
--cometchat-primary-button-background | Button background | #6852D6 |
--cometchat-primary-button-text | Button text color | #FFFFFF |
--cometchat-radius-2 | Border radius | 8px |
--cometchat-border-color-light | Border color | #E0E0E0 |
Custom Color Schemes
/* Blue theme */
.theme-blue cometchat-collaborative-document-bubble {
--cometchat-primary-button-background: #1976D2;
--cometchat-primary-color: #1976D2;
}
/* Green theme */
.theme-green cometchat-collaborative-document-bubble {
--cometchat-primary-button-background: #4CAF50;
--cometchat-primary-color: #4CAF50;
}
/* Dark theme */
[data-theme="dark"] cometchat-collaborative-document-bubble {
--cometchat-background-color-01: #1E1E1E;
--cometchat-background-color-02: #2C2C2C;
--cometchat-text-color-primary: #FFFFFF;
--cometchat-text-color-secondary: #B0B0B0;
--cometchat-border-color-light: #404040;
}
/* Custom button styling */
::ng-deep .cometchat-collaborative-document-bubble__button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 20px;
padding: 10px 20px;
font-weight: 600;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: transform 0.2s, box-shadow 0.2s;
}
::ng-deep .cometchat-collaborative-document-bubble__button:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
Accessibility
The Collaborative Document Bubble component is fully accessible and follows WCAG 2.1 Level AA guidelines.
WCAG 2.1 Compliance
- ✅ 1.1.1 Non-text Content (Level A): Banner image has empty alt attribute (decorative)
- ✅ 1.3.1 Info and Relationships (Level A): Proper semantic structure with button element
- ✅ 2.1.1 Keyboard (Level A): All functionality available via keyboard
- ✅ 2.4.7 Focus Visible (Level AA): Clear focus indicators on action button
- ✅ 4.1.2 Name, Role, Value (Level A): Action button has accessible name via aria-label
Keyboard Support
| Key | Action | Context |
|---|
Tab | Navigate to action button | When component is in focus order |
Shift + Tab | Navigate backwards | When button is focused |
Enter | Activate button | When button is focused |
Space | Activate button | When button is focused |
ARIA Attributes
| Attribute | Element | Value | Purpose |
|---|
aria-label | Action button | Localized button text | Provides accessible name |
alt | Banner image | Empty string | Marks image as decorative |
Screen Reader Behavior
Screen readers announce the component with:
- Banner image: Skipped (decorative)
- Title: “Collaborative Document” (or localized equivalent)
- Subtitle: “Open document to edit content together” (or localized equivalent)
- Button: “Open Document, button” (or localized equivalent)
Best Practices
Always provide the complete CometChat.CustomMessage object to ensure the document URL is correctly extracted from metadata.
The component expects the document URL at metadata["@injected"]["extensions"]["document"]["document_url"]. Ensure your message objects have this structure.
Use the alignment property to distinguish between incoming and outgoing messages for proper visual styling.
Handle the buttonClick event if you need custom behavior beyond opening the URL in a new window.
The action button is automatically disabled when the document URL is missing or empty.
The component automatically selects the appropriate banner image based on the current theme (light/dark).
- CometChatCollaborativeWhiteboardBubble: Displays collaborative whiteboard messages
- CometChatFileBubble: Displays file attachment messages
- CometChatMessageBubble: Parent component for message bubbles
- CometChatMessageList: Displays lists of messages
Technical Details
- Standalone Component: Can be imported and used independently
- Change Detection: Uses OnPush change detection strategy for optimal performance
- Dependencies:
- Angular CommonModule
- CometChat SDK for message types
- TranslatePipe for localization
- BEM CSS: Follows Block Element Modifier naming convention
- Accessibility: WCAG 2.1 Level AA compliant